changeset 121:dfaddaf96f09

did: refactor GPU data types, more generic, prepare to use for decriptors/uniforms
author Sam <sam@basx.dev>
date Fri, 07 Apr 2023 00:32:07 +0700
parents 2780d9aad142
children 506090173619
files src/semicongine/entity.nim src/semicongine/gpu_data.nim src/semicongine/math/matrix.nim src/semicongine/mesh.nim src/semicongine/scene.nim src/semicongine/vulkan/descriptor.nim src/semicongine/vulkan/shader.nim tests/test_vulkan_wrapper.nim
diffstat 8 files changed, 446 insertions(+), 361 deletions(-) [+]
line wrap: on
line diff
--- a/src/semicongine/entity.nim	Thu Apr 06 00:30:56 2023 +0700
+++ b/src/semicongine/entity.nim	Fri Apr 07 00:32:07 2023 +0700
@@ -9,7 +9,7 @@
 
   Entity* = ref object of RootObj
     name*: string
-    transform*: Mat44 # todo: cache transform + only update VBO when transform changed
+    transform*: Mat4 # todo: cache transform + only update VBO when transform changed
     parent*: Entity
     children*: seq[Entity]
     components*: seq[Component]
@@ -37,7 +37,7 @@
 func newEntity*(name: string = ""): Entity =
   result = new Entity
   result.name = name
-  result.transform = Unit44
+  result.transform = Unit4
   if result.name == "":
     result.name = &"Entity[{$(cast[ByteAddress](result))}]"
 
@@ -47,7 +47,7 @@
   for child in children:
     result.add child
   result.name = name
-  result.transform = Unit44
+  result.transform = Unit4
   if result.name == "":
     result.name = &"Entity[{$(cast[ByteAddress](result))}]"
 
@@ -59,10 +59,10 @@
     result.add part
   if result.name == "":
     result.name = &"Entity[{$(cast[ByteAddress](result))}]"
-  result.transform = Unit44
+  result.transform = Unit4
 
-func getModelTransform*(entity: Entity): Mat44 =
-  result = Unit44
+func getModelTransform*(entity: Entity): Mat4 =
+  result = Unit4
   var currentEntity = entity
   while currentEntity != nil:
     result = currentEntity.transform * result
--- a/src/semicongine/gpu_data.nim	Thu Apr 06 00:30:56 2023 +0700
+++ b/src/semicongine/gpu_data.nim	Fri Apr 07 00:32:07 2023 +0700
@@ -7,7 +7,9 @@
 import ./math
 
 type
-  CountType = 1'u32 .. 4'u32
+  GPUType = 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]
+
+type
   DataType* = enum
     Float32
     Float64
@@ -19,47 +21,112 @@
     UInt16
     UInt32
     UInt64
+    Vec2I32
+    Vec2I64
+    Vec3I32
+    Vec3I64
+    Vec4I32
+    Vec4I64
+    Vec2U32
+    Vec2U64
+    Vec3U32
+    Vec3U64
+    Vec4U32
+    Vec4U64
+    Vec2F32
+    Vec2F64
+    Vec3F32
+    Vec3F64
+    Vec4F32
+    Vec4F64
+    Mat2F32
+    Mat2F64
+    Mat23F32
+    Mat23F64
+    Mat32F32
+    Mat32F64
+    Mat3F32
+    Mat3F64
+    Mat34F32
+    Mat34F64
+    Mat43F32
+    Mat43F64
+    Mat4F32
+    Mat4F64
+  DataValue* = object
+    case thetype*: DataType
+    of Float32: float32: float32
+    of Float64: float64: float64
+    of Int8: int8: int8
+    of Int16: int16: int16
+    of Int32: int32: int32
+    of Int64: int64: int64
+    of UInt8: uint8: uint8
+    of UInt16: uint16: uint16
+    of UInt32: uint32: uint32
+    of UInt64: uint64: uint64
+    of Vec2I32: vec2i32: TVec2[int32]
+    of Vec2I64: vec2i64: TVec2[int64]
+    of Vec3I32: vec3i32: TVec3[int32]
+    of Vec3I64: vec3i64: TVec3[int64]
+    of Vec4I32: vec4i32: TVec4[int32]
+    of Vec4I64: vec4i64: TVec4[int64]
+    of Vec2U32: vec2u32: TVec2[uint32]
+    of Vec2U64: vec2u64: TVec2[uint64]
+    of Vec3U32: vec3u32: TVec3[uint32]
+    of Vec3U64: vec3u64: TVec3[uint64]
+    of Vec4U32: vec4u32: TVec4[uint32]
+    of Vec4U64: vec4u64: TVec4[uint64]
+    of Vec2F32: vec2f32: TVec2[float32]
+    of Vec2F64: vec2f64: TVec2[float64]
+    of Vec3F32: vec3f32: TVec3[float32]
+    of Vec3F64: vec3f64: TVec3[float64]
+    of Vec4F32: vec4f32: TVec4[float32]
+    of Vec4F64: vec4f64: TVec4[float64]
+    of Mat2F32: mat2f32: TMat2[float32]
+    of Mat2F64: mat2f64: TMat2[float64]
+    of Mat23F32: mat23f32: TMat23[float32]
+    of Mat23F64: mat23f64: TMat23[float64]
+    of Mat32F32: mat32f32: TMat32[float32]
+    of Mat32F64: mat32f64: TMat32[float64]
+    of Mat3F32: mat3f32: TMat3[float32]
+    of Mat3F64: mat3f64: TMat3[float64]
+    of Mat34F32: mat34f32: TMat34[float32]
+    of Mat34F64: mat34f64: TMat34[float64]
+    of Mat43F32: mat43f32: TMat43[float32]
+    of Mat43F64: mat43f64: TMat43[float64]
+    of Mat4F32: mat4f32: TMat4[float32]
+    of Mat4F64: mat4f64: TMat4[float64]
   MemoryLocation* = enum
     VRAM, VRAMVisible, RAM # VRAM is fastest, VRAMVisible allows updating memory directly, may be slower
-  Attribute* = object
+  VertexAttribute* = object
     name*: string
     thetype*: DataType
-    components*: CountType # how many components the vectors has (1 means scalar)
-    rows*: CountType # used to split matrices into rows of vectors
     perInstance*: bool
     memoryLocation*: MemoryLocation
   AttributeGroup* = object
-    attributes*: seq[Attribute]
+    attributes*: seq[VertexAttribute]
 
-func initAttributeGroup*(attrs: varargs[Attribute]): auto =
+func initAttributeGroup*(attrs: varargs[VertexAttribute]): auto =
   AttributeGroup(attributes: attrs.toSeq)
 
-func vertexInputs*(group: AttributeGroup): seq[Attribute] =
+func vertexInputs*(group: AttributeGroup): seq[VertexAttribute] =
   for attr in group.attributes:
     if attr.perInstance == false:
       result.add attr
 
-func instanceInputs*(group: AttributeGroup): seq[Attribute] =
+func instanceInputs*(group: AttributeGroup): seq[VertexAttribute] =
   for attr in group.attributes:
     if attr.perInstance == false:
       result.add attr
 
-func attr*(
-  name: string,
-  thetype: DataType,
-  components=CountType(1),
-  rows=CountType(1),
-  perInstance=false,
-  memoryLocation=VRAMVisible,
-): auto =
-  Attribute(
-    name: name,
-    thetype: thetype,
-    components: components,
-    rows: rows,
-    perInstance: perInstance,
-    memoryLocation: memoryLocation,
-  )
+
+func numberOfVertexInputAttributeDescriptors*(thetype: DataType): uint32 =
+  case thetype:
+    of Mat2F32, Mat2F64, Mat23F32, Mat23F64: 2
+    of Mat32F32, Mat32F64, Mat3F32, Mat3F64, Mat34F32, Mat34F64: 3
+    of Mat43F32, Mat43F64, Mat4F32, Mat4F64: 4
+    else: 1
 
 func size*(thetype: DataType): uint32 =
   case thetype:
@@ -73,18 +140,48 @@
     of UInt16: 2
     of UInt32: 4
     of UInt64: 8
+    of Vec2I32: 8
+    of Vec2I64: 16
+    of Vec3I32: 12
+    of Vec3I64: 24
+    of Vec4I32: 16
+    of Vec4I64: 32
+    of Vec2U32: 8
+    of Vec2U64: 16
+    of Vec3U32: 12
+    of Vec3U64: 24
+    of Vec4U32: 16
+    of Vec4U64: 32
+    of Vec2F32: 8
+    of Vec2F64: 16
+    of Vec3F32: 12
+    of Vec3F64: 24
+    of Vec4F32: 16
+    of Vec4F64: 32
+    of Mat2F32: 16
+    of Mat2F64: 32
+    of Mat23F32: 24
+    of Mat23F64: 48
+    of Mat32F32: 24
+    of Mat32F64: 48
+    of Mat3F32: 36
+    of Mat3F64: 72
+    of Mat34F32: 48
+    of Mat34F64: 92
+    of Mat43F32: 48
+    of Mat43F64: 92
+    of Mat4F32: 64
+    of Mat4F64: 128
   
-func size*(attribute: Attribute, perRow=false): uint32 =
-  if perRow:
-    attribute.thetype.size * attribute.components
-  else:
-    attribute.thetype.size * attribute.components * attribute.rows
+func size*(attribute: VertexAttribute, perDescriptor=false): uint32 =
+  if perDescriptor: attribute.thetype.size div attribute.thetype.numberOfVertexInputAttributeDescriptors
+  else:      attribute.thetype.size
 
 func size*(thetype: AttributeGroup): uint32 =
   for attribute in thetype.attributes:
     result += attribute.size
 
-func getDataType*[T: SomeNumber](value: T): DataType =
+func getDataType*[T: GPUType|int|uint|float](): DataType =
   when T is float32: Float32
   elif T is float64: Float64
   elif T is int8: Int8
@@ -101,86 +198,149 @@
   elif T is uint and sizeof(uint) == sizeof(uint32): UInt32
   elif T is float and sizeof(float) == sizeof(float32): Float32
   elif T is float and sizeof(float) == sizeof(float64): Float64
+  elif T is TVec2[int32]: Vec2I32
+  elif T is TVec2[int64]: Vec2I64
+  elif T is TVec3[int32]: Vec3I32
+  elif T is TVec3[int64]: Vec3I64
+  elif T is TVec4[int32]: Vec4I32
+  elif T is TVec4[int64]: Vec4I64
+  elif T is TVec2[uint32]: Vec2U32
+  elif T is TVec2[uint64]: Vec2U64
+  elif T is TVec3[uint32]: Vec3U32
+  elif T is TVec3[uint64]: Vec3U64
+  elif T is TVec4[uint32]: Vec4U32
+  elif T is TVec4[uint64]: Vec4U64
+  elif T is TVec2[float32]: Vec2F32
+  elif T is TVec2[float64]: Vec2F64
+  elif T is TVec3[float32]: Vec3F32
+  elif T is TVec3[float64]: Vec3F64
+  elif T is TVec4[float32]: Vec4F32
+  elif T is TVec4[float64]: Vec4F64
+  elif T is TMat2[float32]: Mat2F32
+  elif T is TMat2[float64]: Mat2F64
+  elif T is TMat23[float32]: Mat23F32
+  elif T is TMat23[float64]: Mat23F64
+  elif T is TMat32[float32]: Mat32F32
+  elif T is TMat32[float64]: Mat32F64
+  elif T is TMat3[float32]: Mat3F32
+  elif T is TMat3[float64]: Mat3F64
+  elif T is TMat34[float32]: Mat34F32
+  elif T is TMat34[float64]: Mat34F64
+  elif T is TMat43[float32]: Mat43F32
+  elif T is TMat43[float64]: Mat43F64
+  elif T is TMat4[float32]: Mat4F32
+  elif T is TMat4[float64]: Mat4F64
   else:
     static:
       raise newException(Exception, &"Unsupported data type for GPU data: {name(T)}" )
 
-func asAttribute*[T: SomeNumber|TMat|TVec](value: T, name: string): Attribute =
-  when not (T is SomeNumber):
-    let nonScalarDatatype = getDataType(default(get(genericParams(typeof(value)), 0)))
+func attr*[T: GPUType](
+  name: string,
+  perInstance=false,
+  memoryLocation=VRAMVisible,
+): auto =
+  VertexAttribute(
+    name: name,
+    thetype: getDataType[T](),
+    perInstance: perInstance,
+    memoryLocation: memoryLocation,
+  )
 
-  when T is SomeNumber: attr(name, getDataType(default(T)))
-  elif T is TMat22: attr(name, nonScalarDatatype, 2, 2)
-  elif T is TMat23: attr(name, nonScalarDatatype, 3, 2)
-  elif T is TMat32: attr(name, nonScalarDatatype, 3, 2)
-  elif T is TMat33: attr(name, nonScalarDatatype, 3, 3)
-  elif T is TMat34: attr(name, nonScalarDatatype, 4, 3)
-  elif T is TMat43: attr(name, nonScalarDatatype, 3, 4)
-  elif T is TMat44: attr(name, nonScalarDatatype, 4, 4)
-  elif T is TVec2: attr(name, nonScalarDatatype, 2)
-  elif T is TVec3: attr(name, nonScalarDatatype, 3)
-  elif T is TVec4: attr(name, nonScalarDatatype, 4)
-  else: {.error "Unsupported attribute type for GPU data".}
-
-func asAttribute*[T: SomeNumber|TMat|TVec](): Attribute =
-  asAttribute(default(T), name(T))
+func get*[T: GPUType|int|uint|float](value: DataValue): T =
+  case value.thetype
+    of Float32: value.float32
+    of Float64: value.float64
+    of Int8: value.int8
+    of Int16: value.int16
+    of Int32: value.int32
+    of Int64: value.int64
+    of UInt8: value.uint8
+    of UInt16: value.uint16
+    of UInt32: value.uint32
+    of UInt64: value.uint64
+    of Vec2I32: value.vec2i32
+    of Vec2I64: value.vec2i64
+    of Vec3I32: value.vec3i32
+    of Vec3I64: value.vec3i64
+    of Vec4I32: value.vec4i32
+    of Vec4I64: value.vec4i64
+    of Vec2U32: value.vec2u32
+    of Vec2U64: value.vec2u64
+    of Vec3U32: value.vec3u32
+    of Vec3U64: value.vec3u64
+    of Vec4U32: value.vec4u32
+    of Vec4U64: value.vec4u64
+    of Vec2F32: value.vec2f32
+    of Vec2F64: value.vec2f64
+    of Vec3F32: value.vec3f32
+    of Vec3F64: value.vec3f64
+    of Vec4F32: value.vec4f32
+    of Vec4F64: value.vec4f64
+    of Mat2F32: value.mat2f32
+    of Mat2F64: value.mat2f64
+    of Mat23F32: value.mat23f32
+    of Mat23F64: value.mat23f64
+    of Mat32F32: value.mat32f32
+    of Mat32F64: value.mat32f64
+    of Mat3F32: value.mat3f32
+    of Mat3F64: value.mat3f64
+    of Mat34F32: value.mat34f32
+    of Mat34F64: value.mat34f64
+    of Mat43F32: value.mat43f32
+    of Mat43F64: value.mat43f64
+    of Mat4F32: value.mat4f32
+    of Mat4F64: value.mat4f64
 
 const TYPEMAP = {
-  CountType(1): {
-    UInt8: VK_FORMAT_R8_UINT,
-    Int8: VK_FORMAT_R8_SINT,
-    UInt16: VK_FORMAT_R16_UINT,
-    Int16: VK_FORMAT_R16_SINT,
-    UInt32: VK_FORMAT_R32_UINT,
-    Int32: VK_FORMAT_R32_SINT,
-    UInt64: VK_FORMAT_R64_UINT,
-    Int64: VK_FORMAT_R64_SINT,
     Float32: VK_FORMAT_R32_SFLOAT,
     Float64: VK_FORMAT_R64_SFLOAT,
-  }.toTable,
-  CountType(2): {
-    UInt8: VK_FORMAT_R8G8_UINT,
-    Int8: VK_FORMAT_R8G8_SINT,
-    UInt16: VK_FORMAT_R16G16_UINT,
-    Int16: VK_FORMAT_R16G16_SINT,
-    UInt32: VK_FORMAT_R32G32_UINT,
-    Int32: VK_FORMAT_R32G32_SINT,
-    UInt64: VK_FORMAT_R64G64_UINT,
-    Int64: VK_FORMAT_R64G64_SINT,
-    Float32: VK_FORMAT_R32G32_SFLOAT,
-    Float64: VK_FORMAT_R64G64_SFLOAT,
-  }.toTable,
-  CountType(3): {
-    UInt8: VK_FORMAT_R8G8B8_UINT,
-    Int8: VK_FORMAT_R8G8B8_SINT,
-    UInt16: VK_FORMAT_R16G16B16_UINT,
-    Int16: VK_FORMAT_R16G16B16_SINT,
-    UInt32: VK_FORMAT_R32G32B32_UINT,
-    Int32: VK_FORMAT_R32G32B32_SINT,
-    UInt64: VK_FORMAT_R64G64B64_UINT,
-    Int64: VK_FORMAT_R64G64B64_SINT,
-    Float32: VK_FORMAT_R32G32B32_SFLOAT,
-    Float64: VK_FORMAT_R64G64B64_SFLOAT,
-  }.toTable,
-  CountType(4): {
-    UInt8: VK_FORMAT_R8G8B8A8_UINT,
-    Int8: VK_FORMAT_R8G8B8A8_SINT,
-    UInt16: VK_FORMAT_R16G16B16A16_UINT,
-    Int16: VK_FORMAT_R16G16B16A16_SINT,
-    UInt32: VK_FORMAT_R32G32B32A32_UINT,
-    Int32: VK_FORMAT_R32G32B32A32_SINT,
-    UInt64: VK_FORMAT_R64G64B64A64_UINT,
-    Int64: VK_FORMAT_R64G64B64A64_SINT,
-    Float32: VK_FORMAT_R32G32B32A32_SFLOAT,
-    Float64: VK_FORMAT_R64G64B64A64_SFLOAT,
-  }.toTable,
+    Int8: VK_FORMAT_R8_SINT,
+    Int16: VK_FORMAT_R16_SINT,
+    Int32: VK_FORMAT_R32_SINT,
+    Int64: VK_FORMAT_R64_SINT,
+    UInt8: VK_FORMAT_R8_UINT,
+    UInt16: VK_FORMAT_R16_UINT,
+    UInt32: VK_FORMAT_R32_UINT,
+    UInt64: VK_FORMAT_R64_UINT,
+    Vec2I32: VK_FORMAT_R32G32_SINT,
+    Vec2I64: VK_FORMAT_R64G64_SINT,
+    Vec3I32: VK_FORMAT_R32G32B32_SINT,
+    Vec3I64: VK_FORMAT_R64G64B64_SINT,
+    Vec4I32: VK_FORMAT_R32G32B32A32_SINT,
+    Vec4I64: VK_FORMAT_R64G64B64A64_SINT,
+    Vec2U32: VK_FORMAT_R32G32_UINT,
+    Vec2U64: VK_FORMAT_R64G64_UINT,
+    Vec3U32: VK_FORMAT_R32G32B32_UINT,
+    Vec3U64: VK_FORMAT_R64G64B64_UINT,
+    Vec4U32: VK_FORMAT_R32G32B32A32_UINT,
+    Vec4U64: VK_FORMAT_R64G64B64A64_UINT,
+    Vec2F32: VK_FORMAT_R32G32_SFLOAT,
+    Vec2F64: VK_FORMAT_R64G64_SFLOAT,
+    Vec3F32: VK_FORMAT_R32G32B32_SFLOAT,
+    Vec3F64: VK_FORMAT_R64G64B64_SFLOAT,
+    Vec4F32: VK_FORMAT_R32G32B32A32_SFLOAT,
+    Vec4F64: VK_FORMAT_R64G64B64A64_SFLOAT,
+    Mat2F32: VK_FORMAT_R32G32_SFLOAT,
+    Mat2F64: VK_FORMAT_R64G64_SFLOAT,
+    Mat23F32: VK_FORMAT_R32G32B32_SFLOAT,
+    Mat23F64: VK_FORMAT_R64G64B64_SFLOAT,
+    Mat32F32: VK_FORMAT_R32G32_SFLOAT,
+    Mat32F64: VK_FORMAT_R64G64_SFLOAT,
+    Mat3F32: VK_FORMAT_R32G32B32_SFLOAT,
+    Mat3F64: VK_FORMAT_R64G64B64_SFLOAT,
+    Mat34F32: VK_FORMAT_R32G32B32A32_SFLOAT,
+    Mat34F64: VK_FORMAT_R64G64B64A64_SFLOAT,
+    Mat43F32: VK_FORMAT_R32G32B32_SFLOAT,
+    Mat43F64: VK_FORMAT_R64G64B64_SFLOAT,
+    Mat4F32: VK_FORMAT_R32G32B32A32_SFLOAT,
+    Mat4F64: VK_FORMAT_R64G64B64A64_SFLOAT,
 }.toTable
 
-func getVkFormat*(value: Attribute): VkFormat =
-  TYPEMAP[value.components][value.thetype]
+func getVkFormat*(thetype: DataType): VkFormat =
+  TYPEMAP[thetype]
 
 # from https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap15.html
-func nLocationSlots*(attribute: Attribute): uint32 =
+func nLocationSlots*(thetype: DataType): uint32 =
   #[
   single location:
     16-bit scalar and vector types, and
@@ -189,58 +349,101 @@
   two locations
     64-bit three- and four-component vectors
   ]#
-  case attribute.thetype:
+  case thetype:
     of Float32: 1
-    of Float64: (if attribute.components < 3: 1 else: 2)
+    of Float64: 1
     of Int8: 1
     of Int16: 1
     of Int32: 1
-    of Int64: (if attribute.components < 3: 1 else: 2)
+    of Int64: 1
     of UInt8: 1
     of UInt16: 1
     of UInt32: 1
-    of UInt64: (if attribute.components < 3: 1 else: 2)
+    of UInt64: 1
+    of Vec2I32: 1
+    of Vec2I64: 1
+    of Vec3I32: 1
+    of Vec3I64: 2
+    of Vec4I32: 1
+    of Vec4I64: 2
+    of Vec2U32: 1
+    of Vec2U64: 1
+    of Vec3U32: 1
+    of Vec3U64: 2
+    of Vec4U32: 1
+    of Vec4U64: 2
+    of Vec2F32: 1
+    of Vec2F64: 1
+    of Vec3F32: 1
+    of Vec3F64: 2
+    of Vec4F32: 1
+    of Vec4F64: 2
+    of Mat2F32: 1
+    of Mat2F64: 1
+    of Mat23F32: 1
+    of Mat23F64: 2
+    of Mat32F32: 1
+    of Mat32F64: 1
+    of Mat3F32: 1
+    of Mat3F64: 2
+    of Mat34F32: 1
+    of Mat34F64: 2
+    of Mat43F32: 1
+    of Mat43F64: 2
+    of Mat4F32: 1
+    of Mat4F64: 2
 
-func glslType*(attribute: Attribute): string =
+func glslType*(thetype: DataType): string =
   # 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..
-  
-  # used to ensure square matrix get only one number for side instead of two,  e.g. mat2 instead of mat22
-  let matrixColumns = if attribute.components == attribute.rows: "" else: $attribute.components
-  case attribute.rows:
-    of 1:
-      case attribute.components:
-        of 1: # scalars
-          case attribute.thetype:
-            of Float32: "float"
-            of Float64: "double"
-            of Int8, Int16, Int32, Int64: "int"
-            of UInt8, UInt16, UInt32, UInt64: "uint"
-        else: # vectors
-          case attribute.thetype:
-            of Float32: &"vec{attribute.components}"
-            of Float64: &"dvec{attribute.components}"
-            of Int8, Int16, Int32, Int64: &"ivec{attribute.components}"
-            of UInt8, UInt16, UInt32, UInt64: &"uvec{attribute.components}"
-    else:
-      case attribute.components:
-        of 1: raise newException(Exception, &"Unsupported matrix-column-count: {attribute.components}")
-        else:
-          case attribute.thetype:
-            of Float32: &"mat{attribute.rows}{matrixColumns}"
-            of Float64: &"dmat{attribute.rows}{matrixColumns}"
-            else: raise newException(Exception, &"Unsupported matrix-component type: {attribute.thetype}")
+  case thetype:
+    of Float32: "float"
+    of Float64: "double"
+    of Int8, Int16, Int32, Int64: "int"
+    of UInt8, UInt16, UInt32, UInt64: "uint"
+    of Vec2I32: "ivec2"
+    of Vec2I64: "ivec2"
+    of Vec3I32: "ivec3"
+    of Vec3I64: "ivec3"
+    of Vec4I32: "ivec4"
+    of Vec4I64: "ivec4"
+    of Vec2U32: "uvec2"
+    of Vec2U64: "uvec2"
+    of Vec3U32: "uvec3"
+    of Vec3U64: "uvec3"
+    of Vec4U32: "uvec4"
+    of Vec4U64: "uvec4"
+    of Vec2F32: "vec2"
+    of Vec2F64: "dvec2"
+    of Vec3F32: "vec3"
+    of Vec3F64: "dvec3"
+    of Vec4F32: "vec4"
+    of Vec4F64: "dvec4"
+    of Mat2F32: "mat2"
+    of Mat2F64: "dmat2"
+    of Mat23F32: "mat23"
+    of Mat23F64: "dmat23"
+    of Mat32F32: "mat32"
+    of Mat32F64: "dmat32"
+    of Mat3F32: "mat3"
+    of Mat3F64: "dmat3"
+    of Mat34F32: "mat34"
+    of Mat34F64: "dmat34"
+    of Mat43F32: "mat43"
+    of Mat43F64: "dmat43"
+    of Mat4F32: "mat4"
+    of Mat4F64: "dmat4"
 
 func glslInput*(group: AttributeGroup): seq[string] =
   if group.attributes.len == 0:
     return @[]
   var i = 0'u32
   for attribute in group.attributes:
-    result.add &"layout(location = {i}) in {attribute.glslType} {attribute.name};"
-    for j in 0 ..< attribute.rows:
-      i += attribute.nLocationSlots
+    result.add &"layout(location = {i}) in {attribute.thetype.glslType} {attribute.name};"
+    for j in 0 ..< attribute.thetype.numberOfVertexInputAttributeDescriptors:
+      i += attribute.thetype.nLocationSlots
 
 func glslUniforms*(group: AttributeGroup, blockName="Uniforms", binding=0): seq[string] =
   if group.attributes.len == 0:
@@ -248,7 +451,7 @@
   # currently only a single uniform block supported, therefore binding = 0
   result.add(&"layout(binding = {binding}) uniform T{blockName} {{")
   for attribute in group.attributes:
-    result.add(&"    {attribute.glslType} {attribute.name};")
+    result.add(&"    {attribute.thetype.glslType} {attribute.name};")
   result.add(&"}} {blockName};")
 
 func glslOutput*(group: AttributeGroup): seq[string] =
@@ -256,10 +459,10 @@
     return @[]
   var i = 0'u32
   for attribute in group.attributes:
-    result.add &"layout(location = {i}) out {attribute.glslType} {attribute.name};"
+    result.add &"layout(location = {i}) out {attribute.thetype.glslType} {attribute.name};"
     i += 1
 
-func groupByMemoryLocation*(attributes: openArray[Attribute]): Table[MemoryLocation, seq[Attribute]] =
+func groupByMemoryLocation*(attributes: openArray[VertexAttribute]): Table[MemoryLocation, seq[VertexAttribute]] =
   for attr in attributes:
     if not (attr.memoryLocation in result):
       result[attr.memoryLocation] = @[]
--- a/src/semicongine/math/matrix.nim	Thu Apr 06 00:30:56 2023 +0700
+++ b/src/semicongine/math/matrix.nim	Fri Apr 07 00:32:07 2023 +0700
@@ -16,39 +16,39 @@
   # which prevents the type system from identifying the correct type at times)
   #
   # Though, great news is that objects have zero overhead!
-  TMat22*[T: SomeNumber] = object
+  TMat2*[T: SomeNumber] = object
     data*: array[4, T]
   TMat23*[T: SomeNumber] = object
     data*: array[6, T]
   TMat32*[T: SomeNumber] = object
     data*: array[6, T]
-  TMat33*[T: SomeNumber] = object
+  TMat3*[T: SomeNumber] = object
     data*: array[9, T]
   TMat34*[T: SomeNumber] = object
     data*: array[12, T]
   TMat43*[T: SomeNumber] = object
     data*: array[12, T]
-  TMat44*[T: SomeNumber] = object
+  TMat4*[T: SomeNumber] = object
     data*: array[16, T]
-  TMat* = TMat22|TMat33|TMat44|TMat23|TMat32|TMat34|TMat43
-  Mat22* = TMat22[float32]
+  TMat* = TMat2|TMat3|TMat4|TMat23|TMat32|TMat34|TMat43
+  Mat2* = TMat2[float32]
   Mat23* = TMat23[float32]
   Mat32* = TMat32[float32]
-  Mat33* = TMat33[float32]
+  Mat3* = TMat3[float32]
   Mat34* = TMat34[float32]
   Mat43* = TMat43[float32]
-  Mat44* = TMat44[float32]
+  Mat4* = TMat4[float32]
 
-func unit22[T: SomeNumber](): auto {.compiletime.} = TMat22[T](data:[
+func unit2[T: SomeNumber](): auto {.compiletime.} = TMat2[T](data:[
   T(1), T(0),
   T(0), T(1),
 ])
-func unit33[T: SomeNumber](): auto {.compiletime.} = TMat33[T](data:[
+func unit3[T: SomeNumber](): auto {.compiletime.} = TMat3[T](data:[
   T(1), T(0), T(0),
   T(0), T(1), T(0),
   T(0), T(0), T(1),
 ])
-func unit44[T: SomeNumber](): auto {.compiletime.} = TMat44[T](data: [
+func unit4[T: SomeNumber](): auto {.compiletime.} = TMat4[T](data: [
   T(1), T(0), T(0), T(0),
   T(0), T(1), T(0), T(0),
   T(0), T(0), T(1), T(0),
@@ -67,40 +67,40 @@
     if theType[^1].isDigit:
       typename = typename & theType[^1]
     result.add(newConstStmt(
-      postfix(ident("Unit22" & typename), "*"),
-      newCall(nnkBracketExpr.newTree(ident("unit22"), ident(theType)))
+      postfix(ident("Unit2" & typename), "*"),
+      newCall(nnkBracketExpr.newTree(ident("unit2"), ident(theType)))
     ))
     result.add(newConstStmt(
-      postfix(ident("Unit33" & typename), "*"),
-      newCall(nnkBracketExpr.newTree(ident("unit33"), ident(theType)))
+      postfix(ident("Unit3" & typename), "*"),
+      newCall(nnkBracketExpr.newTree(ident("unit3"), ident(theType)))
     ))
     result.add(newConstStmt(
-      postfix(ident("Unit44" & typename), "*"),
-      newCall(nnkBracketExpr.newTree(ident("unit44"), ident(theType)))
+      postfix(ident("Unit4" & typename), "*"),
+      newCall(nnkBracketExpr.newTree(ident("unit4"), ident(theType)))
     ))
 
 generateAllConsts()
 
-const Unit22* = unit22[float32]()
-const Unit33* = unit33[float32]()
-const Unit44* = unit44[float32]()
+const Unit2* = unit2[float32]()
+const Unit3* = unit3[float32]()
+const Unit4* = unit4[float32]()
 
 template rowCount*(m: typedesc): int =
-  when m is TMat22: 2
+  when m is TMat2: 2
   elif m is TMat23: 2
   elif m is TMat32: 3
-  elif m is TMat33: 3
+  elif m is TMat3: 3
   elif m is TMat34: 3
   elif m is TMat43: 4
-  elif m is TMat44: 4
+  elif m is TMat4: 4
 template columnCount*(m: typedesc): int =
-  when m is TMat22: 2
+  when m is TMat2: 2
   elif m is TMat23: 3
   elif m is TMat32: 2
-  elif m is TMat33: 3
+  elif m is TMat3: 3
   elif m is TMat34: 4
   elif m is TMat43: 3
-  elif m is TMat44: 4
+  elif m is TMat4: 4
 
 
 func toString[T](value: T): string =
@@ -124,32 +124,32 @@
       result &= filler & strvalues[i] & "  "
   result = $T & "\n" & result
 
-func `$`*(v: TMat22[SomeNumber]): string = toString[TMat22[SomeNumber]](v)
+func `$`*(v: TMat2[SomeNumber]): string = toString[TMat2[SomeNumber]](v)
 func `$`*(v: TMat23[SomeNumber]): string = toString[TMat23[SomeNumber]](v)
 func `$`*(v: TMat32[SomeNumber]): string = toString[TMat32[SomeNumber]](v)
-func `$`*(v: TMat33[SomeNumber]): string = toString[TMat33[SomeNumber]](v)
+func `$`*(v: TMat3[SomeNumber]): string = toString[TMat3[SomeNumber]](v)
 func `$`*(v: TMat34[SomeNumber]): string = toString[TMat34[SomeNumber]](v)
 func `$`*(v: TMat43[SomeNumber]): string = toString[TMat43[SomeNumber]](v)
-func `$`*(v: TMat44[SomeNumber]): string = toString[TMat44[SomeNumber]](v)
+func `$`*(v: TMat4[SomeNumber]): string = toString[TMat4[SomeNumber]](v)
 
 func `[]`*[T: TMat](m: T, row, col: int): auto = m.data[col + row * T.columnCount]
 proc `[]=`*[T: TMat, U](m: var T, row, col: int, value: U) = m.data[col + row * T.columnCount] = value
 
-func row*[T: TMat22](m: T, i: 0..1): auto = TVec2([m[i, 0], m[i, 1]])
+func row*[T: TMat2](m: T, i: 0..1): auto = TVec2([m[i, 0], m[i, 1]])
 func row*[T: TMat32](m: T, i: 0..2): auto = TVec2([m[i, 0], m[i, 1]])
 func row*[T: TMat23](m: T, i: 0..1): auto = TVec3([m[i, 0], m[i, 1], m[i, 2]])
-func row*[T: TMat33](m: T, i: 0..2): auto = TVec3([m[i, 0], m[i, 1], m[i, 2]])
+func row*[T: TMat3](m: T, i: 0..2): auto = TVec3([m[i, 0], m[i, 1], m[i, 2]])
 func row*[T: TMat43](m: T, i: 0..3): auto = TVec3([m[i, 0], m[i, 1], m[i, 2]])
 func row*[T: TMat34](m: T, i: 0..2): auto = TVec4([m[i, 0], m[i, 1], m[i, 2], m[i, 3]])
-func row*[T: TMat44](m: T, i: 0..3): auto = TVec4([m[i, 0], m[i, 1], m[i, 2], m[i, 3]])
+func row*[T: TMat4](m: T, i: 0..3): auto = TVec4([m[i, 0], m[i, 1], m[i, 2], m[i, 3]])
 
-func col*[T: TMat22](m: T, i: 0..1): auto = TVec2([m[0, i], m[1, i]])
+func col*[T: TMat2](m: T, i: 0..1): auto = TVec2([m[0, i], m[1, i]])
 func col*[T: TMat23](m: T, i: 0..2): auto = TVec2([m[0, i], m[1, i]])
 func col*[T: TMat32](m: T, i: 0..1): auto = TVec3([m[0, i], m[1, i], m[2, i]])
-func col*[T: TMat33](m: T, i: 0..2): auto = TVec3([m[0, i], m[1, i], m[2, i]])
+func col*[T: TMat3](m: T, i: 0..2): auto = TVec3([m[0, i], m[1, i], m[2, i]])
 func col*[T: TMat34](m: T, i: 0..3): auto = TVec3([m[0, i], m[1, i], m[2, i]])
 func col*[T: TMat43](m: T, i: 0..2): auto = TVec4([m[0, i], m[1, i], m[2, i], m[3, i]])
-func col*[T: TMat44](m: T, i: 0..3): auto = TVec4([m[0, i], m[1, i], m[2, i], m[3, i]])
+func col*[T: TMat4](m: T, i: 0..3): auto = TVec4([m[0, i], m[1, i], m[2, i], m[3, i]])
 
 proc createMatMatMultiplicationOperator(leftType: typedesc, rightType: typedesc, outType: typedesc): NimNode =
   var data = nnkBracket.newTree()
@@ -254,36 +254,36 @@
   result = newStmtList()
 
   for op in ["+", "-", "*", "/"]:
-    result.add(createMatScalarOperator(TMat22, op))
+    result.add(createMatScalarOperator(TMat2, op))
     result.add(createMatScalarOperator(TMat23, op))
     result.add(createMatScalarOperator(TMat32, op))
-    result.add(createMatScalarOperator(TMat33, op))
+    result.add(createMatScalarOperator(TMat3, op))
     result.add(createMatScalarOperator(TMat34, op))
     result.add(createMatScalarOperator(TMat43, op))
-    result.add(createMatScalarOperator(TMat44, op))
+    result.add(createMatScalarOperator(TMat4, op))
 
-  result.add(createMatMatMultiplicationOperator(TMat22, TMat22, TMat22))
-  result.add(createMatMatMultiplicationOperator(TMat22, TMat23, TMat23))
-  result.add(createMatMatMultiplicationOperator(TMat23, TMat32, TMat22))
-  result.add(createMatMatMultiplicationOperator(TMat23, TMat33, TMat23))
-  result.add(createMatMatMultiplicationOperator(TMat32, TMat22, TMat32))
-  result.add(createMatMatMultiplicationOperator(TMat32, TMat23, TMat33))
-  result.add(createMatMatMultiplicationOperator(TMat33, TMat32, TMat32))
-  result.add(createMatMatMultiplicationOperator(TMat33, TMat33, TMat33))
-  result.add(createMatMatMultiplicationOperator(TMat33, TMat34, TMat34))
-  result.add(createMatMatMultiplicationOperator(TMat43, TMat33, TMat43))
-  result.add(createMatMatMultiplicationOperator(TMat43, TMat34, TMat44))
-  result.add(createMatMatMultiplicationOperator(TMat44, TMat43, TMat43))
-  result.add(createMatMatMultiplicationOperator(TMat44, TMat44, TMat44))
+  result.add(createMatMatMultiplicationOperator(TMat2, TMat2, TMat2))
+  result.add(createMatMatMultiplicationOperator(TMat2, TMat23, TMat23))
+  result.add(createMatMatMultiplicationOperator(TMat23, TMat32, TMat2))
+  result.add(createMatMatMultiplicationOperator(TMat23, TMat3, TMat23))
+  result.add(createMatMatMultiplicationOperator(TMat32, TMat2, TMat32))
+  result.add(createMatMatMultiplicationOperator(TMat32, TMat23, TMat3))
+  result.add(createMatMatMultiplicationOperator(TMat3, TMat32, TMat32))
+  result.add(createMatMatMultiplicationOperator(TMat3, TMat3, TMat3))
+  result.add(createMatMatMultiplicationOperator(TMat3, TMat34, TMat34))
+  result.add(createMatMatMultiplicationOperator(TMat43, TMat3, TMat43))
+  result.add(createMatMatMultiplicationOperator(TMat43, TMat34, TMat4))
+  result.add(createMatMatMultiplicationOperator(TMat4, TMat43, TMat43))
+  result.add(createMatMatMultiplicationOperator(TMat4, TMat4, TMat4))
 
-  result.add(createVecMatMultiplicationOperator(TMat22, TVec2))
-  result.add(createVecMatMultiplicationOperator(TMat33, TVec3))
-  result.add(createVecMatMultiplicationOperator(TMat44, TVec4))
+  result.add(createVecMatMultiplicationOperator(TMat2, TVec2))
+  result.add(createVecMatMultiplicationOperator(TMat3, TVec3))
+  result.add(createVecMatMultiplicationOperator(TMat4, TVec4))
 
 createAllMultiplicationOperators()
 
 
-func transposed*[T](m: TMat22[T]): TMat22[T] = TMat22[T](data: [
+func transposed*[T](m: TMat2[T]): TMat2[T] = TMat2[T](data: [
   m[0, 0], m[1, 0],
   m[0, 1], m[1, 1],
 ])
@@ -296,7 +296,7 @@
   m[0, 0], m[1, 0], m[2, 0],
   m[0, 1], m[1, 1], m[2, 1],
 ])
-func transposed*[T](m: TMat33[T]): TMat33[T] = TMat33[T](data: [
+func transposed*[T](m: TMat3[T]): TMat3[T] = TMat3[T](data: [
   m[0, 0], m[1, 0], m[2, 0],
   m[0, 1], m[1, 1], m[2, 1],
   m[0, 2], m[1, 2], m[2, 2],
@@ -312,48 +312,48 @@
   m[0, 2], m[1, 2], m[2, 2],
   m[0, 3], m[1, 3], m[2, 3],
 ])
-func transposed*[T](m: TMat44[T]): TMat44[T] = TMat44[T](data: [
+func transposed*[T](m: TMat4[T]): TMat4[T] = TMat4[T](data: [
   m[0, 0], m[1, 0], m[2, 0], m[3, 0],
   m[0, 1], m[1, 1], m[2, 1], m[3, 1],
   m[0, 2], m[1, 2], m[2, 2], m[3, 2],
   m[0, 3], m[1, 3], m[2, 3], m[3, 3],
 ])
 
-func translate2d*[T](x, y: T): TMat33[T] = TMat33[T](data: [
+func translate2d*[T](x, y: T): TMat3[T] = TMat3[T](data: [
   T(1), T(0), x,
   T(0), T(1), y,
   T(0), T(0), T(1),
 ])
-func scale2d*[T](sx, sy: T): TMat33[T] = TMat33[T](data: [
+func scale2d*[T](sx, sy: T): TMat3[T] = TMat3[T](data: [
   sx, T(0), T(0),
   T(0), sy, T(0),
   T(0), T(0), T(1),
 ])
-func rotate2d*[T](angle: T): TMat33[T] = TMat33[T](data: [
+func rotate2d*[T](angle: T): TMat3[T] = TMat3[T](data: [
   cos(angle), -sin(angle), T(0),
   sin(angle), cos(angle), T(0),
   T(0), T(0), T(1),
 ])
-func translate3d*[T](x, y, z: T): TMat44[T] = TMat44[T](data: [
+func translate3d*[T](x, y, z: T): TMat4[T] = TMat4[T](data: [
   T(1), T(0), T(0), x,
   T(0), T(1), T(0), y,
   T(0), T(0), T(1), z,
   T(0), T(0), T(0), T(1),
 ])
-func scale3d*[T](sx, sy, sz: T): TMat44[T] = TMat44[T](data: [
+func scale3d*[T](sx, sy, sz: T): TMat4[T] = TMat4[T](data: [
   sx, T(0), T(0), T(0),
   T(0), sy, T(0), T(0),
   T(0), T(0), sz, T(0),
   T(0), T(0),  T(0), T(1),
 ])
-func rotate3d*[T](angle: T, a: TVec3[T]): TMat44[T] =
+func rotate3d*[T](angle: T, a: TVec3[T]): TMat4[T] =
   let
     cosa = cos(angle)
     sina = sin(angle)
     x = a[0]
     y = a[1]
     z = a[2]
-  TMat44[T](data: [
+  TMat4[T](data: [
     x * x * (1 - cosa) + cosa,     y * x * (1 - cosa) - z * sina, z * x * (1 - cosa) + y * sina, T(0),
     x * y * (1 - cosa) + z * sina, y * y * (1 - cosa) + cosa,     z * y * (1 - cosa) - x * sina, T(0),
     x * z * (1 - cosa) - y * sina, y * z * (1 - cosa) + x * sina, z * z * (1 - cosa) + cosa,     T(0),
@@ -370,25 +370,25 @@
       for i in 0 ..< result.data.len:
         result.data[i] = rand(1.0)
 
-makeRandomInit(TMat22)
+makeRandomInit(TMat2)
 makeRandomInit(TMat23)
 makeRandomInit(TMat32)
-makeRandomInit(TMat33)
+makeRandomInit(TMat3)
 makeRandomInit(TMat34)
 makeRandomInit(TMat43)
-makeRandomInit(TMat44)
+makeRandomInit(TMat4)
 
-func perspective*[T: SomeFloat](fovy, aspect, zNear, zFar: T): TMat44[T] =
+func perspective*[T: SomeFloat](fovy, aspect, zNear, zFar: T): TMat4[T] =
   let tanHalfFovy = tan(fovy / T(2))
-  return TMat44[T](data:[
+  return TMat4[T](data:[
     T(1) / (aspect * tanHalfFovy), T(0),               T(0),                     T(0),
     T(0),                          T(1) / tanHalfFovy, T(0),                     T(0),
     T(0),                          T(0),               T(zFar / (zFar - zNear)), T(-(zFar * zNear) / (zFar - zNear)),
     T(0),                          T(0),               T(1),                     T(1),
   ])
 
-func ortho*[T: SomeFloat](left, right, top, bottom, zNear, zFar: T): TMat44[T] =
-  TMat44[T](data:[
+func ortho*[T: SomeFloat](left, right, top, bottom, zNear, zFar: T): TMat4[T] =
+  TMat4[T](data:[
     T(2) / (right - left), T(0),                  T(0),                  -(right + left) / (right - left),
     T(0),                  T(2) / (bottom - top), T(0),                  -(bottom + top) / (bottom - top),
     T(0),                  T(0),                  T(1) / (zFar - zNear), -zNear / (zFar - zNear),
--- a/src/semicongine/mesh.nim	Thu Apr 06 00:30:56 2023 +0700
+++ b/src/semicongine/mesh.nim	Fri Apr 07 00:32:07 2023 +0700
@@ -15,7 +15,7 @@
     Position, Color, Normal, Tangent, BiTangent, TextureCoordinate
   MeshIndexType* = enum
     None
-    Tiny # up to 2^8 vertices
+    Tiny # up to 2^8 vertices # TODO: need to check and enable support for this
     Small # up to 2^16 vertices
     Big # up to 2^32 vertices
   MeshData = object
@@ -28,7 +28,7 @@
       of TextureCoordinate: texturecoord: seq[Vec2f]
   Mesh* = ref object of Component
     vertexCount*: uint32
-    data: Table[Attribute, MeshData]
+    data: Table[VertexAttribute, MeshData]
     case indexType*: MeshIndexType
       of None: discard
       of Tiny: tinyIndices*: seq[array[3, uint8]]
@@ -57,9 +57,9 @@
   result = new Mesh
   result.vertexCount = uint32(positions.len)
   result.indexType = None
-  result.data[asAttribute(default(Vec3f), "position")] = MeshData(thetype: Position, position: positions.toSeq)
+  result.data[attr[Vec3f]("position")] = MeshData(thetype: Position, position: positions.toSeq)
   if colors.len > 0:
-    result.data[asAttribute(default(Vec3f), "color")] = MeshData(thetype: Color, color: colors.toSeq)
+    result.data[attr[Vec3f]("color")] = MeshData(thetype: Color, color: colors.toSeq)
 
 
 func newMesh*(positions: openArray[Vec3f], colors: openArray[Vec3f]=[], indices: openArray[array[3, uint32|int32|int]], autoResize=true): auto =
@@ -67,16 +67,16 @@
 
   result = new Mesh
   result.vertexCount = uint32(positions.len)
-  result.data[asAttribute(default(Vec3f), "position")] = MeshData(thetype: Position, position: positions.toSeq)
+  result.data[attr[Vec3f]("position")] = MeshData(thetype: Position, position: positions.toSeq)
   if colors.len > 0:
-    result.data[asAttribute(default(Vec3f), "color")] = MeshData(thetype: Color, color: colors.toSeq)
+    result.data[attr[Vec3f]("color")] = MeshData(thetype: Color, color: colors.toSeq)
 
   for i in indices:
     assert uint32(i[0]) < result.vertexCount
     assert uint32(i[1]) < result.vertexCount
     assert uint32(i[2]) < result.vertexCount
 
-  if autoResize and uint32(positions.len) < uint32(high(uint8)) and false: # todo: check feature support
+  if autoResize and uint32(positions.len) < uint32(high(uint8)) and false: # TODO: check feature support
     result.indexType = Tiny
     for i, tri in enumerate(indices):
       result.tinyIndices.add [uint8(tri[0]), uint8(tri[1]), uint8(tri[2])]
@@ -94,9 +94,9 @@
 
   result = new Mesh
   result.vertexCount = uint32(positions.len)
-  result.data[asAttribute(default(Vec3f), "position")] = MeshData(thetype: Position, position: positions.toSeq)
+  result.data[attr[Vec3f]("position")] = MeshData(thetype: Position, position: positions.toSeq)
   if colors.len > 0:
-    result.data[asAttribute(default(Vec3f), "color")] = MeshData(thetype: Color, color: colors.toSeq)
+    result.data[attr[Vec3f]("color")] = MeshData(thetype: Color, color: colors.toSeq)
 
   for i in indices:
     assert i[0] < result.vertexCount
@@ -108,13 +108,13 @@
 
 func newMesh*(positions: openArray[Vec3f], colors: openArray[Vec3f]=[], indices: openArray[array[3, uint8|int8]]): auto =
   assert colors.len == 0 or colors.len == positions.len
-  assert false # todo: check feature support
+  assert false # TODO: check feature support
 
   result = new Mesh
   result.vertexCount = uint32(positions.len)
-  result.data[asAttribute(default(Vec3f), "position")] = MeshData(thetype: Position, position: positions.toSeq)
+  result.data[attr[Vec3f]("position")] = MeshData(thetype: Position, position: positions.toSeq)
   if colors.len > 0:
-    result.data[asAttribute(default(Vec3f), "color")] = MeshData(thetype: Color, color: colors.toSeq)
+    result.data[attr[Vec3f]("color")] = MeshData(thetype: Color, color: colors.toSeq)
 
   for i in indices:
     assert i[0] < result.vertexCount
@@ -134,7 +134,7 @@
     of BiTangent: meshdata.bitangent.size
     of TextureCoordinate: meshdata.texturecoord.size
 
-func attributeSize*(mesh: Mesh, attribute: Attribute): uint64 =
+func attributeSize*(mesh: Mesh, attribute: VertexAttribute): uint64 =
   mesh.data[attribute].meshDataSize
 
 func vertexDataSize*(mesh: Mesh): uint64 =
@@ -167,137 +167,14 @@
     of Small: rawData(mesh.smallIndices)
     of Big: rawData(mesh.bigIndices)
 
-proc hasDataFor*(mesh: Mesh, attribute: Attribute): bool =
+proc hasDataFor*(mesh: Mesh, attribute: VertexAttribute): bool =
   assert attribute.perInstance == false, "Mesh data cannot handle per-instance attributes"
   attribute in mesh.data
 
-proc getRawData*(mesh: Mesh, attribute: Attribute): (pointer, uint64) =
+proc getRawData*(mesh: Mesh, attribute: VertexAttribute): (pointer, uint64) =
   assert attribute.perInstance == false, "Mesh data cannot handle per-instance attributes"
   mesh.data[attribute].getRawData()
 
-proc getData*(mesh: Mesh, attribute: Attribute): MeshData =
+proc getData*(mesh: Mesh, attribute: VertexAttribute): MeshData =
   assert attribute.perInstance == false, "Mesh data cannot handle per-instance attributes"
   mesh.data[attribute]
-
-#[
-
-func createUberMesh*[T: object, U: uint16|uint32](meshes: openArray[Mesh[
-    T, U]]): Mesh[T, U] =
-  var indexoffset = U(0)
-  for mesh in meshes:
-    for srcname, srcvalue in mesh.vertexData.fieldPairs:
-      for dstname, dstvalue in result.vertexData.fieldPairs:
-        when srcname == dstname:
-          dstvalue.data.add srcvalue.data
-      var indexdata: seq[array[3, U]]
-      for i in mesh.indices:
-        indexdata.add [i[0] + indexoffset, i[1] + indexoffset, i[2] + indexoffset]
-      result.indices.add indexdata
-    indexoffset += U(mesh.vertexData.VertexCount)
-
-func getVkIndexType[T: object, U: uint16|uint32](m: Mesh[T,
-    U]): VkIndexType =
-  when U is uint16: VK_INDEX_TYPE_UINT16
-  elif U is uint32: VK_INDEX_TYPE_UINT32
-
-proc createVertexBuffers*[M: Mesh](
-  mesh: M,
-  device: VkDevice,
-  physicalDevice: VkPhysicalDevice,
-  commandPool: VkCommandPool,
-  queue: VkQueue,
-): (seq[Buffer], uint32) =
-  result[1] = mesh.vertexData.VertexCount
-  for name, value in mesh.vertexData.fieldPairs:
-    assert value.data.len > 0
-    var flags = if value.useOnDeviceMemory: {TransferSrc} else: {VertexBuffer}
-    var stagingBuffer = device.InitBuffer(physicalDevice, value.datasize, flags, {HostVisible, HostCoherent})
-    copyMem(stagingBuffer.data, addr(value.data[0]), value.datasize)
-
-    if value.useOnDeviceMemory:
-      var finalBuffer = device.InitBuffer(physicalDevice, value.datasize, {
-          TransferDst, VertexBuffer}, {DeviceLocal})
-      transferBuffer(commandPool, queue, stagingBuffer, finalBuffer,
-          value.datasize)
-      stagingBuffer.trash()
-      result[0].add(finalBuffer)
-      value.buffer = finalBuffer
-    else:
-      result[0].add(stagingBuffer)
-      value.buffer = stagingBuffer
-
-proc createIndexBuffer*(
-  mesh: Mesh,
-  device: VkDevice,
-  physicalDevice: VkPhysicalDevice,
-  commandPool: VkCommandPool,
-  queue: VkQueue,
-  useDeviceLocalBuffer: bool = true # decides if data is transfered to the fast device-local memory or not
-): Buffer =
-  let bufferSize = uint64(mesh.indices.len * sizeof(get(genericParams(typeof(
-      mesh.indices)), 0)))
-  let flags = if useDeviceLocalBuffer: {TransferSrc} else: {IndexBuffer}
-
-  var stagingBuffer = device.InitBuffer(physicalDevice, bufferSize, flags, {
-      HostVisible, HostCoherent})
-  copyMem(stagingBuffer.data, addr(mesh.indices[0]), bufferSize)
-
-  if useDeviceLocalBuffer:
-    var finalBuffer = device.InitBuffer(physicalDevice, bufferSize, {
-        TransferDst, IndexBuffer}, {DeviceLocal})
-    transferBuffer(commandPool, queue, stagingBuffer, finalBuffer, bufferSize)
-    stagingBuffer.trash()
-    return finalBuffer
-  else:
-    return stagingBuffer
-
-proc createIndexedVertexBuffers*(
-  mesh: Mesh,
-  device: VkDevice,
-  physicalDevice: VkPhysicalDevice,
-  commandPool: VkCommandPool,
-  queue: VkQueue,
-  useDeviceLocalBufferForIndices: bool = true # decides if data is transfered to the fast device-local memory or not
-): (seq[Buffer], bool, Buffer, uint32, VkIndexType) =
-  result[0] = createVertexBuffers(mesh, device, physicalDevice, commandPool,
-      queue)[0]
-  result[1] = mesh.indexed
-  if mesh.indexed:
-    result[2] = createIndexBuffer(mesh, device, physicalDevice, commandPool,
-        queue, useDeviceLocalBufferForIndices)
-    result[3] = uint32(mesh.indices.len * mesh.indices[0].len)
-    result[4] = getVkIndexType(mesh)
-  else:
-    result[3] = uint32(mesh.vertexData.VertexCount)
-
-func quad*[VertexType, VecType, T](): Mesh[VertexType, uint16] =
-  result = new Mesh[VertexType, uint16]
-  result.indexed = true
-  result.indices = @[[0'u16, 1'u16, 2'u16], [2'u16, 3'u16, 0'u16]]
-  result.vertexData = VertexType()
-  for attrname, value in result.vertexData.fieldPairs:
-    when typeof(value) is PositionAttribute:
-      value.data = @[
-        VecType([T(-0.5), T(-0.5), T(0)]),
-        VecType([T(+0.5), T(-0.5), T(0)]),
-        VecType([T(+0.5), T(+0.5), T(0)]),
-        VecType([T(-0.5), T(+0.5), T(0)]),
-      ]
-      value.useOnDeviceMemory = true
-
-func circle*[VertexType, VecType, T](n = 16): Mesh[VertexType, uint16] =
-  result = new Mesh[VertexType, uint16]
-  result.indexed = true
-  let angleStep = (2'f * PI) / float32(n)
-  var data = @[VecType([T(0), T(0), T(0)])]
-  for i in 1 .. n:
-    data.add VecType([T(cos(float32(i) * angleStep)), T(sin(float32(i) *
-        angleStep)), T(0)])
-    result.indices.add [0'u16, uint16(i), uint16(i mod (n) + 1)]
-
-  result.vertexData = VertexType()
-  for attrname, value in result.vertexData.fieldPairs:
-    when typeof(value) is PositionAttribute:
-      value.data = data
-      value.useOnDeviceMemory = true
-]#
--- a/src/semicongine/scene.nim	Thu Apr 06 00:30:56 2023 +0700
+++ b/src/semicongine/scene.nim	Fri Apr 07 00:32:07 2023 +0700
@@ -23,6 +23,10 @@
     of false:
       discard
 
+  ShaderGlobal* = ref object of Component
+    name*: string
+    value*: DataValue
+
   Scene* = object
     name*: string
     root*: Entity
@@ -75,7 +79,8 @@
       of Small: smallIndexedMeshes.add mesh
       of Big: bigIndexedMeshes.add mesh
 
-  allIndexedMeshes = bigIndexedMeshes & smallIndexedMeshes & tinyIndexedMeshes # that we don't have to care about index alignment
+  # ordering meshes this way allows us to ignore value alignment (I think, needs more testing)
+  allIndexedMeshes = bigIndexedMeshes & smallIndexedMeshes & tinyIndexedMeshes
   
   var
     indicesBufferSize = 0'u64
@@ -92,7 +97,7 @@
     )
 
   for location, attributes in pipeline.inputs.vertexInputs.groupByMemoryLocation().pairs:
-    # setup one buffer per attribute location
+    # setup one buffer per attribute-location-type
     var bufferSize = 0'u64
     for mesh in nonIndexedMeshes & allIndexedMeshes:
       bufferSize += mesh.vertexDataSize
--- a/src/semicongine/vulkan/descriptor.nim	Thu Apr 06 00:30:56 2023 +0700
+++ b/src/semicongine/vulkan/descriptor.nim	Fri Apr 07 00:32:07 2023 +0700
@@ -6,12 +6,12 @@
 import ./utils
 
 type
-  Descriptor* = object # "fields" of a DescriptorSet
+  Descriptor* = object # "fields" of a DescriptorSetLayout
     thetype*: VkDescriptorType
     count*: uint32
     stages*: seq[VkShaderStageFlagBits]
     itemsize*: uint32
-  DescriptorSetLayout* = object # "type description of a DescriptorSet
+  DescriptorSetLayout* = object # "type-description" of a DescriptorSet
     device: Device
     vk*: VkDescriptorSetLayout
     descriptors*: seq[Descriptor]
--- a/src/semicongine/vulkan/shader.nim	Thu Apr 06 00:30:56 2023 +0700
+++ b/src/semicongine/vulkan/shader.nim	Fri Apr 07 00:32:07 2023 +0700
@@ -157,14 +157,14 @@
       inputRate: if attribute.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 ..< attribute.rows:
+    for i in 0 ..< attribute.thetype.numberOfVertexInputAttributeDescriptors:
       attributes.add VkVertexInputAttributeDescription(
         binding: binding,
         location: location,
-        format: getVkFormat(attribute),
-        offset: i * attribute.size(perRow=true),
+        format: attribute.thetype.getVkFormat,
+        offset: i * attribute.size(perDescriptor=true),
       )
-      location += attribute.nLocationSlots
+      location += attribute.thetype.nLocationSlots
     inc binding
 
   return VkPipelineVertexInputStateCreateInfo(
--- a/tests/test_vulkan_wrapper.nim	Thu Apr 06 00:30:56 2023 +0700
+++ b/tests/test_vulkan_wrapper.nim	Fri Apr 07 00:32:07 2023 +0700
@@ -56,11 +56,11 @@
   # INIT RENDERER:
   const
     vertexInput = initAttributeGroup(
-      asAttribute(default(Vec3f), "position"),
-      asAttribute(default(Vec3f), "color"),
+      attr[Vec3f]("position"),
+      attr[Vec3f]("color"),
     )
-    vertexOutput = initAttributeGroup(asAttribute(default(Vec3f), "outcolor"))
-    fragOutput = initAttributeGroup(asAttribute(default(Vec4f), "color"))
+    vertexOutput = initAttributeGroup(attr[Vec3f]("outcolor"))
+    fragOutput = initAttributeGroup(attr[Vec4f]("color"))
     vertexCode = compileGlslShader(
       stage=VK_SHADER_STAGE_VERTEX_BIT,
       inputs=vertexInput,