changeset 330:04531bec3583

did: remove some stuff from the heap, maybe nicer?
author Sam <sam@basx.dev>
date Sat, 02 Sep 2023 23:51:02 +0700
parents 69e18f69713b
children 05fb85ba97dd
files src/semicongine/core/gpu_data.nim src/semicongine/core/utils.nim src/semicongine/engine.nim src/semicongine/mesh.nim src/semicongine/renderer.nim src/semicongine/resources/mesh.nim src/semicongine/text.nim src/semicongine/vulkan/drawable.nim tests/test_font.nim tests/test_vulkan_wrapper.nim
diffstat 10 files changed, 656 insertions(+), 599 deletions(-) [+]
line wrap: on
line diff
--- a/src/semicongine/core/gpu_data.nim	Tue Aug 29 00:01:13 2023 +0700
+++ b/src/semicongine/core/gpu_data.nim	Sat Sep 02 23:51:02 2023 +0700
@@ -5,6 +5,7 @@
 import ./vulkanapi
 import ./vector
 import ./matrix
+import ./utils
 
 type
   Sampler2DType* = object
@@ -101,48 +102,48 @@
   DataList* = object
     len*: int
     case theType*: DataType
-    of Float32: float32: ref seq[float32]
-    of Float64: float64: ref seq[float64]
-    of Int8: int8: ref seq[int8]
-    of Int16: int16: ref seq[int16]
-    of Int32: int32: ref seq[int32]
-    of Int64: int64: ref seq[int64]
-    of UInt8: uint8: ref seq[uint8]
-    of UInt16: uint16: ref seq[uint16]
-    of UInt32: uint32: ref seq[uint32]
-    of UInt64: uint64: ref seq[uint64]
-    of Vec2I32: vec2i32: ref seq[TVec2[int32]]
-    of Vec2I64: vec2i64: ref seq[TVec2[int64]]
-    of Vec3I32: vec3i32: ref seq[TVec3[int32]]
-    of Vec3I64: vec3i64: ref seq[TVec3[int64]]
-    of Vec4I32: vec4i32: ref seq[TVec4[int32]]
-    of Vec4I64: vec4i64: ref seq[TVec4[int64]]
-    of Vec2U32: vec2u32: ref seq[TVec2[uint32]]
-    of Vec2U64: vec2u64: ref seq[TVec2[uint64]]
-    of Vec3U32: vec3u32: ref seq[TVec3[uint32]]
-    of Vec3U64: vec3u64: ref seq[TVec3[uint64]]
-    of Vec4U32: vec4u32: ref seq[TVec4[uint32]]
-    of Vec4U64: vec4u64: ref seq[TVec4[uint64]]
-    of Vec2F32: vec2f32: ref seq[TVec2[float32]]
-    of Vec2F64: vec2f64: ref seq[TVec2[float64]]
-    of Vec3F32: vec3f32: ref seq[TVec3[float32]]
-    of Vec3F64: vec3f64: ref seq[TVec3[float64]]
-    of Vec4F32: vec4f32: ref seq[TVec4[float32]]
-    of Vec4F64: vec4f64: ref seq[TVec4[float64]]
-    of Mat2F32: mat2f32: ref seq[TMat2[float32]]
-    of Mat2F64: mat2f64: ref seq[TMat2[float64]]
-    of Mat23F32: mat23f32: ref seq[TMat23[float32]]
-    of Mat23F64: mat23f64: ref seq[TMat23[float64]]
-    of Mat32F32: mat32f32: ref seq[TMat32[float32]]
-    of Mat32F64: mat32f64: ref seq[TMat32[float64]]
-    of Mat3F32: mat3f32: ref seq[TMat3[float32]]
-    of Mat3F64: mat3f64: ref seq[TMat3[float64]]
-    of Mat34F32: mat34f32: ref seq[TMat34[float32]]
-    of Mat34F64: mat34f64: ref seq[TMat34[float64]]
-    of Mat43F32: mat43f32: ref seq[TMat43[float32]]
-    of Mat43F64: mat43f64: ref seq[TMat43[float64]]
-    of Mat4F32: mat4f32*: ref seq[TMat4[float32]]
-    of Mat4F64: mat4f64: ref seq[TMat4[float64]]
+    of Float32: float32: seq[float32]
+    of Float64: float64: seq[float64]
+    of Int8: int8: seq[int8]
+    of Int16: int16: seq[int16]
+    of Int32: int32: seq[int32]
+    of Int64: int64: seq[int64]
+    of UInt8: uint8: seq[uint8]
+    of UInt16: uint16: seq[uint16]
+    of UInt32: uint32: seq[uint32]
+    of UInt64: uint64: seq[uint64]
+    of Vec2I32: vec2i32: seq[TVec2[int32]]
+    of Vec2I64: vec2i64: seq[TVec2[int64]]
+    of Vec3I32: vec3i32: seq[TVec3[int32]]
+    of Vec3I64: vec3i64: seq[TVec3[int64]]
+    of Vec4I32: vec4i32: seq[TVec4[int32]]
+    of Vec4I64: vec4i64: seq[TVec4[int64]]
+    of Vec2U32: vec2u32: seq[TVec2[uint32]]
+    of Vec2U64: vec2u64: seq[TVec2[uint64]]
+    of Vec3U32: vec3u32: seq[TVec3[uint32]]
+    of Vec3U64: vec3u64: seq[TVec3[uint64]]
+    of Vec4U32: vec4u32: seq[TVec4[uint32]]
+    of Vec4U64: vec4u64: seq[TVec4[uint64]]
+    of Vec2F32: vec2f32: seq[TVec2[float32]]
+    of Vec2F64: vec2f64: seq[TVec2[float64]]
+    of Vec3F32: vec3f32: seq[TVec3[float32]]
+    of Vec3F64: vec3f64: seq[TVec3[float64]]
+    of Vec4F32: vec4f32: seq[TVec4[float32]]
+    of Vec4F64: vec4f64: seq[TVec4[float64]]
+    of Mat2F32: mat2f32: seq[TMat2[float32]]
+    of Mat2F64: mat2f64: seq[TMat2[float64]]
+    of Mat23F32: mat23f32: seq[TMat23[float32]]
+    of Mat23F64: mat23f64: seq[TMat23[float64]]
+    of Mat32F32: mat32f32: seq[TMat32[float32]]
+    of Mat32F64: mat32f64: seq[TMat32[float64]]
+    of Mat3F32: mat3f32: seq[TMat3[float32]]
+    of Mat3F64: mat3f64: seq[TMat3[float64]]
+    of Mat34F32: mat34f32: seq[TMat34[float32]]
+    of Mat34F64: mat34f64: seq[TMat34[float64]]
+    of Mat43F32: mat43f32: seq[TMat43[float32]]
+    of Mat43F64: mat43f64: seq[TMat43[float64]]
+    of Mat4F32: mat4f32*: seq[TMat4[float32]]
+    of Mat4F64: mat4f64: seq[TMat4[float64]]
     of Sampler2D: discard
   MemoryPerformanceHint* = enum
     PreferFastRead, PreferFastWrite
@@ -154,52 +155,6 @@
     noInterpolation: bool
     memoryPerformanceHint*: MemoryPerformanceHint
 
-func hash*(value: DataList): Hash =
-  case value.theType
-    of Float32: hash(cast[pointer](value.float32))
-    of Float64: hash(cast[pointer](value.float64))
-    of Int8: hash(cast[pointer](value.int8))
-    of Int16: hash(cast[pointer](value.int16))
-    of Int32: hash(cast[pointer](value.int32))
-    of Int64: hash(cast[pointer](value.int64))
-    of UInt8: hash(cast[pointer](value.uint8))
-    of UInt16: hash(cast[pointer](value.uint16))
-    of UInt32: hash(cast[pointer](value.uint32))
-    of UInt64: hash(cast[pointer](value.uint64))
-    of Vec2I32: hash(cast[pointer](value.vec2i32))
-    of Vec2I64: hash(cast[pointer](value.vec2i64))
-    of Vec3I32: hash(cast[pointer](value.vec3i32))
-    of Vec3I64: hash(cast[pointer](value.vec3i64))
-    of Vec4I32: hash(cast[pointer](value.vec4i32))
-    of Vec4I64: hash(cast[pointer](value.vec4i64))
-    of Vec2U32: hash(cast[pointer](value.vec2u32))
-    of Vec2U64: hash(cast[pointer](value.vec2u64))
-    of Vec3U32: hash(cast[pointer](value.vec3u32))
-    of Vec3U64: hash(cast[pointer](value.vec3u64))
-    of Vec4U32: hash(cast[pointer](value.vec4u32))
-    of Vec4U64: hash(cast[pointer](value.vec4u64))
-    of Vec2F32: hash(cast[pointer](value.vec2f32))
-    of Vec2F64: hash(cast[pointer](value.vec2f64))
-    of Vec3F32: hash(cast[pointer](value.vec3f32))
-    of Vec3F64: hash(cast[pointer](value.vec3f64))
-    of Vec4F32: hash(cast[pointer](value.vec4f32))
-    of Vec4F64: hash(cast[pointer](value.vec4f64))
-    of Mat2F32: hash(cast[pointer](value.mat2f32))
-    of Mat2F64: hash(cast[pointer](value.mat2f64))
-    of Mat23F32: hash(cast[pointer](value.mat23f32))
-    of Mat23F64: hash(cast[pointer](value.mat23f64))
-    of Mat32F32: hash(cast[pointer](value.mat32f32))
-    of Mat32F64: hash(cast[pointer](value.mat32f64))
-    of Mat3F32: hash(cast[pointer](value.mat3f32))
-    of Mat3F64: hash(cast[pointer](value.mat3f64))
-    of Mat34F32: hash(cast[pointer](value.mat34f32))
-    of Mat34F64: hash(cast[pointer](value.mat34f64))
-    of Mat43F32: hash(cast[pointer](value.mat43f32))
-    of Mat43F64: hash(cast[pointer](value.mat43f64))
-    of Mat4F32: hash(cast[pointer](value.mat4f32))
-    of Mat4F64: hash(cast[pointer](value.mat4f64))
-    of Sampler2D: raise newException(Exception, "hash not defined for Sampler2D")
-
 func `==`*(a, b: DataList | DataValue): bool =
   if a.theType != b.theType:
     return false
@@ -453,105 +408,101 @@
 
 func setValues*[T: GPUType|int|uint|float](value: var DataList, data: seq[T]) =
   value.len = data.len
-  when T is float32: value.float32[] = data
-  elif T is float64: value.float64[] = data
-  elif T is int8: value.int8[] = data
-  elif T is int16: value.int16[] = data
-  elif T is int32: value.int32[] = data
-  elif T is int64: value.int64[] = data
-  elif T is uint8: value.uint8[] = data
-  elif T is uint16: value.uint16[] = data
-  elif T is uint32: value.uint32[] = data
-  elif T is uint64: value.uint64[] = data
-  elif T is int and sizeof(int) == sizeof(int32): value.int32[] = data
-  elif T is int and sizeof(int) == sizeof(int64): value.int64[] = data
-  elif T is uint and sizeof(uint) == sizeof(uint32): value.uint32[] = data
-  elif T is uint and sizeof(uint) == sizeof(uint64): value.uint64[] = data
-  elif T is float and sizeof(float) == sizeof(float32): value.float32[] = data
-  elif T is float and sizeof(float) == sizeof(float64): value.float64[] = data
-  elif T is TVec2[int32]: value.vec2i32[] = data
-  elif T is TVec2[int64]: value.vec2i64[] = data
-  elif T is TVec3[int32]: value.vec3i32[] = data
-  elif T is TVec3[int64]: value.vec3i64[] = data
-  elif T is TVec4[int32]: value.vec4i32[] = data
-  elif T is TVec4[int64]: value.vec4i64[] = data
-  elif T is TVec2[uint32]: value.vec2u32[] = data
-  elif T is TVec2[uint64]: value.vec2u64[] = data
-  elif T is TVec3[uint32]: value.vec3u32[] = data
-  elif T is TVec3[uint64]: value.vec3u64[] = data
-  elif T is TVec4[uint32]: value.vec4u32[] = data
-  elif T is TVec4[uint64]: value.vec4u64[] = data
-  elif T is TVec2[float32]: value.vec2f32[] = data
-  elif T is TVec2[float64]: value.vec2f64[] = data
-  elif T is TVec3[float32]: value.vec3f32[] = data
-  elif T is TVec3[float64]: value.vec3f64[] = data
-  elif T is TVec4[float32]: value.vec4f32[] = data
-  elif T is TVec4[float64]: value.vec4f64[] = data
-  elif T is TMat2[float32]: value.mat2f32[] = data
-  elif T is TMat2[float64]: value.mat2f64[] = data
-  elif T is TMat23[float32]: value.mat23f32[] = data
-  elif T is TMat23[float64]: value.mat23f64[] = data
-  elif T is TMat32[float32]: value.mat32f32[] = data
-  elif T is TMat32[float64]: value.mat32f64[] = data
-  elif T is TMat3[float32]: value.mat3f32[] = data
-  elif T is TMat3[float64]: value.mat3f64[] = data
-  elif T is TMat34[float32]: value.mat34f32[] = data
-  elif T is TMat34[float64]: value.mat34f64[] = data
-  elif T is TMat43[float32]: value.mat43f32[] = data
-  elif T is TMat43[float64]: value.mat43f64[] = data
-  elif T is TMat4[float32]: value.mat4f32[] = data
-  elif T is TMat4[float64]: value.mat4f64[] = data
+  when T is float32: value.float32 = data
+  elif T is float64: value.float64 = data
+  elif T is int8: value.int8 = data
+  elif T is int16: value.int16 = data
+  elif T is int32: value.int32 = data
+  elif T is int64: value.int64 = data
+  elif T is uint8: value.uint8 = data
+  elif T is uint16: value.uint16 = data
+  elif T is uint32: value.uint32 = data
+  elif T is uint64: value.uint64 = data
+  elif T is int and sizeof(int) == sizeof(int32): value.int32 = data
+  elif T is int and sizeof(int) == sizeof(int64): value.int64 = data
+  elif T is uint and sizeof(uint) == sizeof(uint32): value.uint32 = data
+  elif T is uint and sizeof(uint) == sizeof(uint64): value.uint64 = data
+  elif T is float and sizeof(float) == sizeof(float32): value.float32 = data
+  elif T is float and sizeof(float) == sizeof(float64): value.float64 = data
+  elif T is TVec2[int32]: value.vec2i32 = data
+  elif T is TVec2[int64]: value.vec2i64 = data
+  elif T is TVec3[int32]: value.vec3i32 = data
+  elif T is TVec3[int64]: value.vec3i64 = data
+  elif T is TVec4[int32]: value.vec4i32 = data
+  elif T is TVec4[int64]: value.vec4i64 = data
+  elif T is TVec2[uint32]: value.vec2u32 = data
+  elif T is TVec2[uint64]: value.vec2u64 = data
+  elif T is TVec3[uint32]: value.vec3u32 = data
+  elif T is TVec3[uint64]: value.vec3u64 = data
+  elif T is TVec4[uint32]: value.vec4u32 = data
+  elif T is TVec4[uint64]: value.vec4u64 = data
+  elif T is TVec2[float32]: value.vec2f32 = data
+  elif T is TVec2[float64]: value.vec2f64 = data
+  elif T is TVec3[float32]: value.vec3f32 = data
+  elif T is TVec3[float64]: value.vec3f64 = data
+  elif T is TVec4[float32]: value.vec4f32 = data
+  elif T is TVec4[float64]: value.vec4f64 = data
+  elif T is TMat2[float32]: value.mat2f32 = data
+  elif T is TMat2[float64]: value.mat2f64 = data
+  elif T is TMat23[float32]: value.mat23f32 = data
+  elif T is TMat23[float64]: value.mat23f64 = data
+  elif T is TMat32[float32]: value.mat32f32 = data
+  elif T is TMat32[float64]: value.mat32f64 = data
+  elif T is TMat3[float32]: value.mat3f32 = data
+  elif T is TMat3[float64]: value.mat3f64 = data
+  elif T is TMat34[float32]: value.mat34f32 = data
+  elif T is TMat34[float64]: value.mat34f64 = data
+  elif T is TMat43[float32]: value.mat43f32 = data
+  elif T is TMat43[float64]: value.mat43f64 = data
+  elif T is TMat4[float32]: value.mat4f32 = data
+  elif T is TMat4[float64]: value.mat4f64 = data
   else: {. error: "Virtual datatype has no values" .}
 
-func toGPUValue*[T: GPUType](value: T): DataValue =
-  result = DataValue(theType: getDataType[T]())
-  result.setValue(value)
-
 func newDataList*(theType: DataType): DataList =
   result = DataList(theType: theType)
   case result.theType
-    of Float32: result.float32 = new seq[float32]
-    of Float64: result.float64 = new seq[float64]
-    of Int8: result.int8 = new seq[int8]
-    of Int16: result.int16 = new seq[int16]
-    of Int32: result.int32 = new seq[int32]
-    of Int64: result.int64 = new seq[int64]
-    of UInt8: result.uint8 = new seq[uint8]
-    of UInt16: result.uint16 = new seq[uint16]
-    of UInt32: result.uint32 = new seq[uint32]
-    of UInt64: result.uint64 = new seq[uint64]
-    of Vec2I32: result.vec2i32 = new seq[TVec2[int32]]
-    of Vec2I64: result.vec2i64 = new seq[TVec2[int64]]
-    of Vec3I32: result.vec3i32 = new seq[TVec3[int32]]
-    of Vec3I64: result.vec3i64 = new seq[TVec3[int64]]
-    of Vec4I32: result.vec4i32 = new seq[TVec4[int32]]
-    of Vec4I64: result.vec4i64 = new seq[TVec4[int64]]
-    of Vec2U32: result.vec2u32 = new seq[TVec2[uint32]]
-    of Vec2U64: result.vec2u64 = new seq[TVec2[uint64]]
-    of Vec3U32: result.vec3u32 = new seq[TVec3[uint32]]
-    of Vec3U64: result.vec3u64 = new seq[TVec3[uint64]]
-    of Vec4U32: result.vec4u32 = new seq[TVec4[uint32]]
-    of Vec4U64: result.vec4u64 = new seq[TVec4[uint64]]
-    of Vec2F32: result.vec2f32 = new seq[TVec2[float32]]
-    of Vec2F64: result.vec2f64 = new seq[TVec2[float64]]
-    of Vec3F32: result.vec3f32 = new seq[TVec3[float32]]
-    of Vec3F64: result.vec3f64 = new seq[TVec3[float64]]
-    of Vec4F32: result.vec4f32 = new seq[TVec4[float32]]
-    of Vec4F64: result.vec4f64 = new seq[TVec4[float64]]
-    of Mat2F32: result.mat2f32 = new seq[TMat2[float32]]
-    of Mat2F64: result.mat2f64 = new seq[TMat2[float64]]
-    of Mat23F32: result.mat23f32 = new seq[TMat23[float32]]
-    of Mat23F64: result.mat23f64 = new seq[TMat23[float64]]
-    of Mat32F32: result.mat32f32 = new seq[TMat32[float32]]
-    of Mat32F64: result.mat32f64 = new seq[TMat32[float64]]
-    of Mat3F32: result.mat3f32 = new seq[TMat3[float32]]
-    of Mat3F64: result.mat3f64 = new seq[TMat3[float64]]
-    of Mat34F32: result.mat34f32 = new seq[TMat34[float32]]
-    of Mat34F64: result.mat34f64 = new seq[TMat34[float64]]
-    of Mat43F32: result.mat43f32 = new seq[TMat43[float32]]
-    of Mat43F64: result.mat43f64 = new seq[TMat43[float64]]
-    of Mat4F32: result.mat4f32 = new seq[TMat4[float32]]
-    of Mat4F64: result.mat4f64 = new seq[TMat4[float64]]
+    of Float32: result.float32 = newSeq[float32]()
+    of Float64: result.float64 = newSeq[float64]()
+    of Int8: result.int8 = newSeq[int8]()
+    of Int16: result.int16 = newSeq[int16]()
+    of Int32: result.int32 = newSeq[int32]()
+    of Int64: result.int64 = newSeq[int64]()
+    of UInt8: result.uint8 = newSeq[uint8]()
+    of UInt16: result.uint16 = newSeq[uint16]()
+    of UInt32: result.uint32 = newSeq[uint32]()
+    of UInt64: result.uint64 = newSeq[uint64]()
+    of Vec2I32: result.vec2i32 = newSeq[TVec2[int32]]()
+    of Vec2I64: result.vec2i64 = newSeq[TVec2[int64]]()
+    of Vec3I32: result.vec3i32 = newSeq[TVec3[int32]]()
+    of Vec3I64: result.vec3i64 = newSeq[TVec3[int64]]()
+    of Vec4I32: result.vec4i32 = newSeq[TVec4[int32]]()
+    of Vec4I64: result.vec4i64 = newSeq[TVec4[int64]]()
+    of Vec2U32: result.vec2u32 = newSeq[TVec2[uint32]]()
+    of Vec2U64: result.vec2u64 = newSeq[TVec2[uint64]]()
+    of Vec3U32: result.vec3u32 = newSeq[TVec3[uint32]]()
+    of Vec3U64: result.vec3u64 = newSeq[TVec3[uint64]]()
+    of Vec4U32: result.vec4u32 = newSeq[TVec4[uint32]]()
+    of Vec4U64: result.vec4u64 = newSeq[TVec4[uint64]]()
+    of Vec2F32: result.vec2f32 = newSeq[TVec2[float32]]()
+    of Vec2F64: result.vec2f64 = newSeq[TVec2[float64]]()
+    of Vec3F32: result.vec3f32 = newSeq[TVec3[float32]]()
+    of Vec3F64: result.vec3f64 = newSeq[TVec3[float64]]()
+    of Vec4F32: result.vec4f32 = newSeq[TVec4[float32]]()
+    of Vec4F64: result.vec4f64 = newSeq[TVec4[float64]]()
+    of Mat2F32: result.mat2f32 = newSeq[TMat2[float32]]()
+    of Mat2F64: result.mat2f64 = newSeq[TMat2[float64]]()
+    of Mat23F32: result.mat23f32 = newSeq[TMat23[float32]]()
+    of Mat23F64: result.mat23f64 = newSeq[TMat23[float64]]()
+    of Mat32F32: result.mat32f32 = newSeq[TMat32[float32]]()
+    of Mat32F64: result.mat32f64 = newSeq[TMat32[float64]]()
+    of Mat3F32: result.mat3f32 = newSeq[TMat3[float32]]()
+    of Mat3F64: result.mat3f64 = newSeq[TMat3[float64]]()
+    of Mat34F32: result.mat34f32 = newSeq[TMat34[float32]]()
+    of Mat34F64: result.mat34f64 = newSeq[TMat34[float64]]()
+    of Mat43F32: result.mat43f32 = newSeq[TMat43[float32]]()
+    of Mat43F64: result.mat43f64 = newSeq[TMat43[float64]]()
+    of Mat4F32: result.mat4f32 = newSeq[TMat4[float32]]()
+    of Mat4F64: result.mat4f64 = newSeq[TMat4[float64]]()
     of Sampler2D: discard
 
 func newDataList*[T: GPUType](len=0): DataList =
@@ -563,7 +514,15 @@
   result = newDataList(getDataType[T]())
   setValues[T](result, data)
 
-func getValues*[T: GPUType|int|uint|float](value: DataList): ref seq[T] =
+func toGPUValue*[T: GPUType](value: T): DataValue =
+  result = DataValue(theType: getDataType[T]())
+  result.setValue(value)
+
+func toGPUValue*[T: GPUType](value: seq[T]): DataList =
+  result = newDataList[T](value.len)
+  result.setValue(value)
+
+func getValues*[T: GPUType|int|uint|float](value: DataList): seq[T] =
   when T is float32: value.float32
   elif T is float64: value.float64
   elif T is int8: value.int8
@@ -614,7 +573,58 @@
   elif T is TMat4[float64]: value.mat4f64
   else: {. error: "Virtual datatype has no values" .}
 
-func getRawData*(value: DataValue): (pointer, int) =
+func getValue*[T: GPUType|int|uint|float](value: DataList, i: int): T =
+  when T is float32: value.float32[i]
+  elif T is float64: value.float64[i]
+  elif T is int8: value.int8[i]
+  elif T is int16: value.int16[i]
+  elif T is int32: value.int32[i]
+  elif T is int64: value.int64[i]
+  elif T is uint8: value.uint8[i]
+  elif T is uint16: value.uint16[i]
+  elif T is uint32: value.uint32[i]
+  elif T is uint64: value.uint64[i]
+  elif T is int and sizeof(int) == sizeof(int32): value.int32[i]
+  elif T is int and sizeof(int) == sizeof(int64): value.int64[i]
+  elif T is uint and sizeof(uint) == sizeof(uint32): value.uint32[i]
+  elif T is uint and sizeof(uint) == sizeof(uint64): value.uint64[i]
+  elif T is float and sizeof(float) == sizeof(float32): value.float32[i]
+  elif T is float and sizeof(float) == sizeof(float64): value.float64[i]
+  elif T is TVec2[int32]: value.vec2i32[i]
+  elif T is TVec2[int64]: value.vec2i64[i]
+  elif T is TVec3[int32]: value.vec3i32[i]
+  elif T is TVec3[int64]: value.vec3i64[i]
+  elif T is TVec4[int32]: value.vec4i32[i]
+  elif T is TVec4[int64]: value.vec4i64[i]
+  elif T is TVec2[uint32]: value.vec2u32[i]
+  elif T is TVec2[uint64]: value.vec2u64[i]
+  elif T is TVec3[uint32]: value.vec3u32[i]
+  elif T is TVec3[uint64]: value.vec3u64[i]
+  elif T is TVec4[uint32]: value.vec4u32[i]
+  elif T is TVec4[uint64]: value.vec4u64[i]
+  elif T is TVec2[float32]: value.vec2f32[i]
+  elif T is TVec2[float64]: value.vec2f64[i]
+  elif T is TVec3[float32]: value.vec3f32[i]
+  elif T is TVec3[float64]: value.vec3f64[i]
+  elif T is TVec4[float32]: value.vec4f32[i]
+  elif T is TVec4[float64]: value.vec4f64[i]
+  elif T is TMat2[float32]: value.mat2f32[i]
+  elif T is TMat2[float64]: value.mat2f64[i]
+  elif T is TMat23[float32]: value.mat23f[i]
+  elif T is TMat23[float64]: value.mat23f64[i]
+  elif T is TMat32[float32]: value.mat32f32[i]
+  elif T is TMat32[float64]: value.mat32f64[i]
+  elif T is TMat3[float32]: value.mat3f32[i]
+  elif T is TMat3[float64]: value.mat3f64[i]
+  elif T is TMat34[float32]: value.mat34f32[i]
+  elif T is TMat34[float64]: value.mat34f64[i]
+  elif T is TMat43[float32]: value.mat43f32[i]
+  elif T is TMat43[float64]: value.mat43f64[i]
+  elif T is TMat4[float32]: value.mat4f32[i]
+  elif T is TMat4[float64]: value.mat4f64[i]
+  else: {. error: "Virtual datatype has no values" .}
+
+func getRawData*(value: var DataValue): (pointer, int) =
   result[1] = value.theType.size
   case value.theType
     of Float32: result[0] = addr value.float32
@@ -661,100 +671,100 @@
     of Mat4F64: result[0] = addr value.mat4f64
     of Sampler2D: result[0] = nil
 
-func getRawData*(value: DataList): (pointer, int) =
+func getRawData*(value: var DataList): (pointer, int) =
   if value.len == 0:
     return (nil, 0)
   result[1] = value.theType.size * value.len
   case value.theType
-    of Float32: result[0] = addr value.float32[][0]
-    of Float64: result[0] = addr value.float64[][0]
-    of Int8: result[0] = addr value.int8[][0]
-    of Int16: result[0] = addr value.int16[][0]
-    of Int32: result[0] = addr value.int32[][0]
-    of Int64: result[0] = addr value.int64[][0]
-    of UInt8: result[0] = addr value.uint8[][0]
-    of UInt16: result[0] = addr value.uint16[][0]
-    of UInt32: result[0] = addr value.uint32[][0]
-    of UInt64: result[0] = addr value.uint64[][0]
-    of Vec2I32: result[0] = addr value.vec2i32[][0]
-    of Vec2I64: result[0] = addr value.vec2i64[][0]
-    of Vec3I32: result[0] = addr value.vec3i32[][0]
-    of Vec3I64: result[0] = addr value.vec3i64[][0]
-    of Vec4I32: result[0] = addr value.vec4i32[][0]
-    of Vec4I64: result[0] = addr value.vec4i64[][0]
-    of Vec2U32: result[0] = addr value.vec2u32[][0]
-    of Vec2U64: result[0] = addr value.vec2u64[][0]
-    of Vec3U32: result[0] = addr value.vec3u32[][0]
-    of Vec3U64: result[0] = addr value.vec3u64[][0]
-    of Vec4U32: result[0] = addr value.vec4u32[][0]
-    of Vec4U64: result[0] = addr value.vec4u64[][0]
-    of Vec2F32: result[0] = addr value.vec2f32[][0]
-    of Vec2F64: result[0] = addr value.vec2f64[][0]
-    of Vec3F32: result[0] = addr value.vec3f32[][0]
-    of Vec3F64: result[0] = addr value.vec3f64[][0]
-    of Vec4F32: result[0] = addr value.vec4f32[][0]
-    of Vec4F64: result[0] = addr value.vec4f64[][0]
-    of Mat2F32: result[0] = addr value.mat2f32[][0]
-    of Mat2F64: result[0] = addr value.mat2f64[][0]
-    of Mat23F32: result[0] = addr value.mat23f32[][0]
-    of Mat23F64: result[0] = addr value.mat23f64[][0]
-    of Mat32F32: result[0] = addr value.mat32f32[][0]
-    of Mat32F64: result[0] = addr value.mat32f64[][0]
-    of Mat3F32: result[0] = addr value.mat3f32[][0]
-    of Mat3F64: result[0] = addr value.mat3f64[][0]
-    of Mat34F32: result[0] = addr value.mat34f32[][0]
-    of Mat34F64: result[0] = addr value.mat34f64[][0]
-    of Mat43F32: result[0] = addr value.mat43f32[][0]
-    of Mat43F64: result[0] = addr value.mat43f64[][0]
-    of Mat4F32: result[0] = addr value.mat4f32[][0]
-    of Mat4F64: result[0] = addr value.mat4f64[][0]
+    of Float32: result[0] = value.float32.toCPointer
+    of Float64: result[0] = value.float64.toCPointer
+    of Int8: result[0] = value.int8.toCPointer
+    of Int16: result[0] = value.int16.toCPointer
+    of Int32: result[0] = value.int32.toCPointer
+    of Int64: result[0] = value.int64.toCPointer
+    of UInt8: result[0] = value.uint8.toCPointer
+    of UInt16: result[0] = value.uint16.toCPointer
+    of UInt32: result[0] = value.uint32.toCPointer
+    of UInt64: result[0] = value.uint64.toCPointer
+    of Vec2I32: result[0] = value.vec2i32.toCPointer
+    of Vec2I64: result[0] = value.vec2i64.toCPointer
+    of Vec3I32: result[0] = value.vec3i32.toCPointer
+    of Vec3I64: result[0] = value.vec3i64.toCPointer
+    of Vec4I32: result[0] = value.vec4i32.toCPointer
+    of Vec4I64: result[0] = value.vec4i64.toCPointer
+    of Vec2U32: result[0] = value.vec2u32.toCPointer
+    of Vec2U64: result[0] = value.vec2u64.toCPointer
+    of Vec3U32: result[0] = value.vec3u32.toCPointer
+    of Vec3U64: result[0] = value.vec3u64.toCPointer
+    of Vec4U32: result[0] = value.vec4u32.toCPointer
+    of Vec4U64: result[0] = value.vec4u64.toCPointer
+    of Vec2F32: result[0] = value.vec2f32.toCPointer
+    of Vec2F64: result[0] = value.vec2f64.toCPointer
+    of Vec3F32: result[0] = value.vec3f32.toCPointer
+    of Vec3F64: result[0] = value.vec3f64.toCPointer
+    of Vec4F32: result[0] = value.vec4f32.toCPointer
+    of Vec4F64: result[0] = value.vec4f64.toCPointer
+    of Mat2F32: result[0] = value.mat2f32.toCPointer
+    of Mat2F64: result[0] = value.mat2f64.toCPointer
+    of Mat23F32: result[0] = value.mat23f32.toCPointer
+    of Mat23F64: result[0] = value.mat23f64.toCPointer
+    of Mat32F32: result[0] = value.mat32f32.toCPointer
+    of Mat32F64: result[0] = value.mat32f64.toCPointer
+    of Mat3F32: result[0] = value.mat3f32.toCPointer
+    of Mat3F64: result[0] = value.mat3f64.toCPointer
+    of Mat34F32: result[0] = value.mat34f32.toCPointer
+    of Mat34F64: result[0] = value.mat34f64.toCPointer
+    of Mat43F32: result[0] = value.mat43f32.toCPointer
+    of Mat43F64: result[0] = value.mat43f64.toCPointer
+    of Mat4F32: result[0] = value.mat4f32.toCPointer
+    of Mat4F64: result[0] = value.mat4f64.toCPointer
     of Sampler2D: result[0] = nil
 
 func initData*(value: var DataList, len: int) =
   value.len = len
   case value.theType
-    of Float32: value.float32[].setLen(len)
-    of Float64: value.float64[].setLen(len)
-    of Int8: value.int8[].setLen(len)
-    of Int16: value.int16[].setLen(len)
-    of Int32: value.int32[].setLen(len)
-    of Int64: value.int64[].setLen(len)
-    of UInt8: value.uint8[].setLen(len)
-    of UInt16: value.uint16[].setLen(len)
-    of UInt32: value.uint32[].setLen(len)
-    of UInt64: value.uint64[].setLen(len)
-    of Vec2I32: value.vec2i32[].setLen(len)
-    of Vec2I64: value.vec2i64[].setLen(len)
-    of Vec3I32: value.vec3i32[].setLen(len)
-    of Vec3I64: value.vec3i64[].setLen(len)
-    of Vec4I32: value.vec4i32[].setLen(len)
-    of Vec4I64: value.vec4i64[].setLen(len)
-    of Vec2U32: value.vec2u32[].setLen(len)
-    of Vec2U64: value.vec2u64[].setLen(len)
-    of Vec3U32: value.vec3u32[].setLen(len)
-    of Vec3U64: value.vec3u64[].setLen(len)
-    of Vec4U32: value.vec4u32[].setLen(len)
-    of Vec4U64: value.vec4u64[].setLen(len)
-    of Vec2F32: value.vec2f32[].setLen(len)
-    of Vec2F64: value.vec2f64[].setLen(len)
-    of Vec3F32: value.vec3f32[].setLen(len)
-    of Vec3F64: value.vec3f64[].setLen(len)
-    of Vec4F32: value.vec4f32[].setLen(len)
-    of Vec4F64: value.vec4f64[].setLen(len)
-    of Mat2F32: value.mat2f32[].setLen(len)
-    of Mat2F64: value.mat2f64[].setLen(len)
-    of Mat23F32: value.mat23f32[].setLen(len)
-    of Mat23F64: value.mat23f64[].setLen(len)
-    of Mat32F32: value.mat32f32[].setLen(len)
-    of Mat32F64: value.mat32f64[].setLen(len)
-    of Mat3F32: value.mat3f32[].setLen(len)
-    of Mat3F64: value.mat3f64[].setLen(len)
-    of Mat34F32: value.mat34f32[].setLen(len)
-    of Mat34F64: value.mat34f64[].setLen(len)
-    of Mat43F32: value.mat43f32[].setLen(len)
-    of Mat43F64: value.mat43f64[].setLen(len)
-    of Mat4F32: value.mat4f32[].setLen(len)
-    of Mat4F64: value.mat4f64[].setLen(len)
+    of Float32: value.float32.setLen(len)
+    of Float64: value.float64.setLen(len)
+    of Int8: value.int8.setLen(len)
+    of Int16: value.int16.setLen(len)
+    of Int32: value.int32.setLen(len)
+    of Int64: value.int64.setLen(len)
+    of UInt8: value.uint8.setLen(len)
+    of UInt16: value.uint16.setLen(len)
+    of UInt32: value.uint32.setLen(len)
+    of UInt64: value.uint64.setLen(len)
+    of Vec2I32: value.vec2i32.setLen(len)
+    of Vec2I64: value.vec2i64.setLen(len)
+    of Vec3I32: value.vec3i32.setLen(len)
+    of Vec3I64: value.vec3i64.setLen(len)
+    of Vec4I32: value.vec4i32.setLen(len)
+    of Vec4I64: value.vec4i64.setLen(len)
+    of Vec2U32: value.vec2u32.setLen(len)
+    of Vec2U64: value.vec2u64.setLen(len)
+    of Vec3U32: value.vec3u32.setLen(len)
+    of Vec3U64: value.vec3u64.setLen(len)
+    of Vec4U32: value.vec4u32.setLen(len)
+    of Vec4U64: value.vec4u64.setLen(len)
+    of Vec2F32: value.vec2f32.setLen(len)
+    of Vec2F64: value.vec2f64.setLen(len)
+    of Vec3F32: value.vec3f32.setLen(len)
+    of Vec3F64: value.vec3f64.setLen(len)
+    of Vec4F32: value.vec4f32.setLen(len)
+    of Vec4F64: value.vec4f64.setLen(len)
+    of Mat2F32: value.mat2f32.setLen(len)
+    of Mat2F64: value.mat2f64.setLen(len)
+    of Mat23F32: value.mat23f32.setLen(len)
+    of Mat23F64: value.mat23f64.setLen(len)
+    of Mat32F32: value.mat32f32.setLen(len)
+    of Mat32F64: value.mat32f64.setLen(len)
+    of Mat3F32: value.mat3f32.setLen(len)
+    of Mat3F64: value.mat3f64.setLen(len)
+    of Mat34F32: value.mat34f32.setLen(len)
+    of Mat34F64: value.mat34f64.setLen(len)
+    of Mat43F32: value.mat43f32.setLen(len)
+    of Mat43F64: value.mat43f64.setLen(len)
+    of Mat4F32: value.mat4f32.setLen(len)
+    of Mat4F64: value.mat4f64.setLen(len)
     of Sampler2D: discard
 
 func setValue*[T: GPUType|int|uint|float](value: var DataValue, data: T) =
@@ -810,202 +820,253 @@
 
 func appendValues*[T: GPUType|int|uint|float](value: var DataList, data: seq[T]) =
   value.len += data.len
-  when T is float32: value.float32[].add data
-  elif T is float64: value.float64[].add data
-  elif T is int8: value.int8[].add data
-  elif T is int16: value.int16[].add data
-  elif T is int32: value.int32[].add data
-  elif T is int64: value.int64[].add data
-  elif T is uint8: value.uint8[].add data
-  elif T is uint16: value.uint16[].add data
-  elif T is uint32: value.uint32[].add data
-  elif T is uint64: value.uint64[].add data
-  elif T is int and sizeof(int) == sizeof(int32): value.int32[].add data
-  elif T is int and sizeof(int) == sizeof(int64): value.int64[].add data
-  elif T is uint and sizeof(uint) == sizeof(uint32): value.uint32[].add data
-  elif T is uint and sizeof(uint) == sizeof(uint64): value.uint64[].add data
-  elif T is float and sizeof(float) == sizeof(float32): value.float32[].add data
-  elif T is float and sizeof(float) == sizeof(float64): value.float64[].add data
-  elif T is TVec2[int32]: value.vec2i32[].add data
-  elif T is TVec2[int64]: value.vec2i64[].add data
-  elif T is TVec3[int32]: value.vec3i32[].add data
-  elif T is TVec3[int64]: value.vec3i64[].add data
-  elif T is TVec4[int32]: value.vec4i32[].add data
-  elif T is TVec4[int64]: value.vec4i64[].add data
-  elif T is TVec2[uint32]: value.vec2u32[].add data
-  elif T is TVec2[uint64]: value.vec2u64[].add data
-  elif T is TVec3[uint32]: value.vec3u32[].add data
-  elif T is TVec3[uint64]: value.vec3u64[].add data
-  elif T is TVec4[uint32]: value.vec4u32[].add data
-  elif T is TVec4[uint64]: value.vec4u64[].add data
-  elif T is TVec2[float32]: value.vec2f32[].add data
-  elif T is TVec2[float64]: value.vec2f64[].add data
-  elif T is TVec3[float32]: value.vec3f32[].add data
-  elif T is TVec3[float64]: value.vec3f64[].add data
-  elif T is TVec4[float32]: value.vec4f32[].add data
-  elif T is TVec4[float64]: value.vec4f64[].add data
-  elif T is TMat2[float32]: value.mat2f32[].add data
-  elif T is TMat2[float64]: value.mat2f64[].add data
-  elif T is TMat23[float32]: value.mat23f32[].add data
-  elif T is TMat23[float64]: value.mat23f64[].add data
-  elif T is TMat32[float32]: value.mat32f32[].add data
-  elif T is TMat32[float64]: value.mat32f64[].add data
-  elif T is TMat3[float32]: value.mat3f32[].add data
-  elif T is TMat3[float64]: value.mat3f64[].add data
-  elif T is TMat34[float32]: value.mat34f32[].add data
-  elif T is TMat34[float64]: value.mat34f64[].add data
-  elif T is TMat43[float32]: value.mat43f32[].add data
-  elif T is TMat43[float64]: value.mat43f64[].add data
-  elif T is TMat4[float32]: value.mat4f32[].add data
-  elif T is TMat4[float64]: value.mat4f64[].add data
+  when T is float32: value.float32.add data
+  elif T is float64: value.float64.add data
+  elif T is int8: value.int8.add data
+  elif T is int16: value.int16.add data
+  elif T is int32: value.int32.add data
+  elif T is int64: value.int64.add data
+  elif T is uint8: value.uint8.add data
+  elif T is uint16: value.uint16.add data
+  elif T is uint32: value.uint32.add data
+  elif T is uint64: value.uint64.add data
+  elif T is int and sizeof(int) == sizeof(int32): value.int32.add data
+  elif T is int and sizeof(int) == sizeof(int64): value.int64.add data
+  elif T is uint and sizeof(uint) == sizeof(uint32): value.uint32.add data
+  elif T is uint and sizeof(uint) == sizeof(uint64): value.uint64.add data
+  elif T is float and sizeof(float) == sizeof(float32): value.float32.add data
+  elif T is float and sizeof(float) == sizeof(float64): value.float64.add data
+  elif T is TVec2[int32]: value.vec2i32.add data
+  elif T is TVec2[int64]: value.vec2i64.add data
+  elif T is TVec3[int32]: value.vec3i32.add data
+  elif T is TVec3[int64]: value.vec3i64.add data
+  elif T is TVec4[int32]: value.vec4i32.add data
+  elif T is TVec4[int64]: value.vec4i64.add data
+  elif T is TVec2[uint32]: value.vec2u32.add data
+  elif T is TVec2[uint64]: value.vec2u64.add data
+  elif T is TVec3[uint32]: value.vec3u32.add data
+  elif T is TVec3[uint64]: value.vec3u64.add data
+  elif T is TVec4[uint32]: value.vec4u32.add data
+  elif T is TVec4[uint64]: value.vec4u64.add data
+  elif T is TVec2[float32]: value.vec2f32.add data
+  elif T is TVec2[float64]: value.vec2f64.add data
+  elif T is TVec3[float32]: value.vec3f32.add data
+  elif T is TVec3[float64]: value.vec3f64.add data
+  elif T is TVec4[float32]: value.vec4f32.add data
+  elif T is TVec4[float64]: value.vec4f64.add data
+  elif T is TMat2[float32]: value.mat2f32.add data
+  elif T is TMat2[float64]: value.mat2f64.add data
+  elif T is TMat23[float32]: value.mat23f32.add data
+  elif T is TMat23[float64]: value.mat23f64.add data
+  elif T is TMat32[float32]: value.mat32f32.add data
+  elif T is TMat32[float64]: value.mat32f64.add data
+  elif T is TMat3[float32]: value.mat3f32.add data
+  elif T is TMat3[float64]: value.mat3f64.add data
+  elif T is TMat34[float32]: value.mat34f32.add data
+  elif T is TMat34[float64]: value.mat34f64.add data
+  elif T is TMat43[float32]: value.mat43f32.add data
+  elif T is TMat43[float64]: value.mat43f64.add data
+  elif T is TMat4[float32]: value.mat4f32.add data
+  elif T is TMat4[float64]: value.mat4f64.add data
   else: {. error: "Virtual datatype has no values" .}
 
 func appendValues*(value: var DataList, data: DataList) =
   assert value.theType == data.theType
   value.len += data.len
   case value.theType:
-  of Float32: value.float32[].add data.float32[]
-  of Float64: value.float64[].add data.float64[]
-  of Int8: value.int8[].add data.int8[]
-  of Int16: value.int16[].add data.int16[]
-  of Int32: value.int32[].add data.int32[]
-  of Int64: value.int64[].add data.int64[]
-  of UInt8: value.uint8[].add data.uint8[]
-  of UInt16: value.uint16[].add data.uint16[]
-  of UInt32: value.uint32[].add data.uint32[]
-  of UInt64: value.uint64[].add data.uint64[]
-  of Vec2I32: value.vec2i32[].add data.vec2i32[]
-  of Vec2I64: value.vec2i64[].add data.vec2i64[]
-  of Vec3I32: value.vec3i32[].add data.vec3i32[]
-  of Vec3I64: value.vec3i64[].add data.vec3i64[]
-  of Vec4I32: value.vec4i32[].add data.vec4i32[]
-  of Vec4I64: value.vec4i64[].add data.vec4i64[]
-  of Vec2U32: value.vec2u32[].add data.vec2u32[]
-  of Vec2U64: value.vec2u64[].add data.vec2u64[]
-  of Vec3U32: value.vec3u32[].add data.vec3u32[]
-  of Vec3U64: value.vec3u64[].add data.vec3u64[]
-  of Vec4U32: value.vec4u32[].add data.vec4u32[]
-  of Vec4U64: value.vec4u64[].add data.vec4u64[]
-  of Vec2F32: value.vec2f32[].add data.vec2f32[]
-  of Vec2F64: value.vec2f64[].add data.vec2f64[]
-  of Vec3F32: value.vec3f32[].add data.vec3f32[]
-  of Vec3F64: value.vec3f64[].add data.vec3f64[]
-  of Vec4F32: value.vec4f32[].add data.vec4f32[]
-  of Vec4F64: value.vec4f64[].add data.vec4f64[]
-  of Mat2F32: value.mat2f32[].add data.mat2f32[]
-  of Mat2F64: value.mat2f64[].add data.mat2f64[]
-  of Mat23F32: value.mat23f32[].add data.mat23f32[]
-  of Mat23F64: value.mat23f64[].add data.mat23f64[]
-  of Mat32F32: value.mat32f32[].add data.mat32f32[]
-  of Mat32F64: value.mat32f64[].add data.mat32f64[]
-  of Mat3F32: value.mat3f32[].add data.mat3f32[]
-  of Mat3F64: value.mat3f64[].add data.mat3f64[]
-  of Mat34F32: value.mat34f32[].add data.mat34f32[]
-  of Mat34F64: value.mat34f64[].add data.mat34f64[]
-  of Mat43F32: value.mat43f32[].add data.mat43f32[]
-  of Mat43F64: value.mat43f64[].add data.mat43f64[]
-  of Mat4F32: value.mat4f32[].add data.mat4f32[]
-  of Mat4F64: value.mat4f64[].add data.mat4f64[]
+  of Float32: value.float32.add data.float32
+  of Float64: value.float64.add data.float64
+  of Int8: value.int8.add data.int8
+  of Int16: value.int16.add data.int16
+  of Int32: value.int32.add data.int32
+  of Int64: value.int64.add data.int64
+  of UInt8: value.uint8.add data.uint8
+  of UInt16: value.uint16.add data.uint16
+  of UInt32: value.uint32.add data.uint32
+  of UInt64: value.uint64.add data.uint64
+  of Vec2I32: value.vec2i32.add data.vec2i32
+  of Vec2I64: value.vec2i64.add data.vec2i64
+  of Vec3I32: value.vec3i32.add data.vec3i32
+  of Vec3I64: value.vec3i64.add data.vec3i64
+  of Vec4I32: value.vec4i32.add data.vec4i32
+  of Vec4I64: value.vec4i64.add data.vec4i64
+  of Vec2U32: value.vec2u32.add data.vec2u32
+  of Vec2U64: value.vec2u64.add data.vec2u64
+  of Vec3U32: value.vec3u32.add data.vec3u32
+  of Vec3U64: value.vec3u64.add data.vec3u64
+  of Vec4U32: value.vec4u32.add data.vec4u32
+  of Vec4U64: value.vec4u64.add data.vec4u64
+  of Vec2F32: value.vec2f32.add data.vec2f32
+  of Vec2F64: value.vec2f64.add data.vec2f64
+  of Vec3F32: value.vec3f32.add data.vec3f32
+  of Vec3F64: value.vec3f64.add data.vec3f64
+  of Vec4F32: value.vec4f32.add data.vec4f32
+  of Vec4F64: value.vec4f64.add data.vec4f64
+  of Mat2F32: value.mat2f32.add data.mat2f32
+  of Mat2F64: value.mat2f64.add data.mat2f64
+  of Mat23F32: value.mat23f32.add data.mat23f32
+  of Mat23F64: value.mat23f64.add data.mat23f64
+  of Mat32F32: value.mat32f32.add data.mat32f32
+  of Mat32F64: value.mat32f64.add data.mat32f64
+  of Mat3F32: value.mat3f32.add data.mat3f32
+  of Mat3F64: value.mat3f64.add data.mat3f64
+  of Mat34F32: value.mat34f32.add data.mat34f32
+  of Mat34F64: value.mat34f64.add data.mat34f64
+  of Mat43F32: value.mat43f32.add data.mat43f32
+  of Mat43F64: value.mat43f64.add data.mat43f64
+  of Mat4F32: value.mat4f32.add data.mat4f32
+  of Mat4F64: value.mat4f64.add data.mat4f64
   else: raise newException(Exception, &"Unsupported data type for GPU data:" )
 
 func appendValue*(value: var DataList, data: DataValue) =
   assert value.theType == data.theType, &"appendValue expected {value.theType} but got {data.theType}"
   value.len += 1
   case value.theType:
-  of Float32: value.float32[].add data.float32
-  of Float64: value.float64[].add data.float64
-  of Int8: value.int8[].add data.int8
-  of Int16: value.int16[].add data.int16
-  of Int32: value.int32[].add data.int32
-  of Int64: value.int64[].add data.int64
-  of UInt8: value.uint8[].add data.uint8
-  of UInt16: value.uint16[].add data.uint16
-  of UInt32: value.uint32[].add data.uint32
-  of UInt64: value.uint64[].add data.uint64
-  of Vec2I32: value.vec2i32[].add data.vec2i32
-  of Vec2I64: value.vec2i64[].add data.vec2i64
-  of Vec3I32: value.vec3i32[].add data.vec3i32
-  of Vec3I64: value.vec3i64[].add data.vec3i64
-  of Vec4I32: value.vec4i32[].add data.vec4i32
-  of Vec4I64: value.vec4i64[].add data.vec4i64
-  of Vec2U32: value.vec2u32[].add data.vec2u32
-  of Vec2U64: value.vec2u64[].add data.vec2u64
-  of Vec3U32: value.vec3u32[].add data.vec3u32
-  of Vec3U64: value.vec3u64[].add data.vec3u64
-  of Vec4U32: value.vec4u32[].add data.vec4u32
-  of Vec4U64: value.vec4u64[].add data.vec4u64
-  of Vec2F32: value.vec2f32[].add data.vec2f32
-  of Vec2F64: value.vec2f64[].add data.vec2f64
-  of Vec3F32: value.vec3f32[].add data.vec3f32
-  of Vec3F64: value.vec3f64[].add data.vec3f64
-  of Vec4F32: value.vec4f32[].add data.vec4f32
-  of Vec4F64: value.vec4f64[].add data.vec4f64
-  of Mat2F32: value.mat2f32[].add data.mat2f32
-  of Mat2F64: value.mat2f64[].add data.mat2f64
-  of Mat23F32: value.mat23f32[].add data.mat23f32
-  of Mat23F64: value.mat23f64[].add data.mat23f64
-  of Mat32F32: value.mat32f32[].add data.mat32f32
-  of Mat32F64: value.mat32f64[].add data.mat32f64
-  of Mat3F32: value.mat3f32[].add data.mat3f32
-  of Mat3F64: value.mat3f64[].add data.mat3f64
-  of Mat34F32: value.mat34f32[].add data.mat34f32
-  of Mat34F64: value.mat34f64[].add data.mat34f64
-  of Mat43F32: value.mat43f32[].add data.mat43f32
-  of Mat43F64: value.mat43f64[].add data.mat43f64
-  of Mat4F32: value.mat4f32[].add data.mat4f32
-  of Mat4F64: value.mat4f64[].add data.mat4f64
+  of Float32: value.float32.add data.float32
+  of Float64: value.float64.add data.float64
+  of Int8: value.int8.add data.int8
+  of Int16: value.int16.add data.int16
+  of Int32: value.int32.add data.int32
+  of Int64: value.int64.add data.int64
+  of UInt8: value.uint8.add data.uint8
+  of UInt16: value.uint16.add data.uint16
+  of UInt32: value.uint32.add data.uint32
+  of UInt64: value.uint64.add data.uint64
+  of Vec2I32: value.vec2i32.add data.vec2i32
+  of Vec2I64: value.vec2i64.add data.vec2i64
+  of Vec3I32: value.vec3i32.add data.vec3i32
+  of Vec3I64: value.vec3i64.add data.vec3i64
+  of Vec4I32: value.vec4i32.add data.vec4i32
+  of Vec4I64: value.vec4i64.add data.vec4i64
+  of Vec2U32: value.vec2u32.add data.vec2u32
+  of Vec2U64: value.vec2u64.add data.vec2u64
+  of Vec3U32: value.vec3u32.add data.vec3u32
+  of Vec3U64: value.vec3u64.add data.vec3u64
+  of Vec4U32: value.vec4u32.add data.vec4u32
+  of Vec4U64: value.vec4u64.add data.vec4u64
+  of Vec2F32: value.vec2f32.add data.vec2f32
+  of Vec2F64: value.vec2f64.add data.vec2f64
+  of Vec3F32: value.vec3f32.add data.vec3f32
+  of Vec3F64: value.vec3f64.add data.vec3f64
+  of Vec4F32: value.vec4f32.add data.vec4f32
+  of Vec4F64: value.vec4f64.add data.vec4f64
+  of Mat2F32: value.mat2f32.add data.mat2f32
+  of Mat2F64: value.mat2f64.add data.mat2f64
+  of Mat23F32: value.mat23f32.add data.mat23f32
+  of Mat23F64: value.mat23f64.add data.mat23f64
+  of Mat32F32: value.mat32f32.add data.mat32f32
+  of Mat32F64: value.mat32f64.add data.mat32f64
+  of Mat3F32: value.mat3f32.add data.mat3f32
+  of Mat3F64: value.mat3f64.add data.mat3f64
+  of Mat34F32: value.mat34f32.add data.mat34f32
+  of Mat34F64: value.mat34f64.add data.mat34f64
+  of Mat43F32: value.mat43f32.add data.mat43f32
+  of Mat43F64: value.mat43f64.add data.mat43f64
+  of Mat4F32: value.mat4f32.add data.mat4f32
+  of Mat4F64: value.mat4f64.add data.mat4f64
   else: raise newException(Exception, &"Unsupported data type for GPU data:" )
 
+func setValue*[T: GPUType|int|uint|float](value: var DataList, data: seq[T]) =
+  when T is float32: value.float32 = data
+  elif T is float64: value.float64 = data
+  elif T is int8: value.int8 = data
+  elif T is int16: value.int16 = data
+  elif T is int32: value.int32 = data
+  elif T is int64: value.int64 = data
+  elif T is uint8: value.uint8 = data
+  elif T is uint16: value.uint16 = data
+  elif T is uint32: value.uint32 = data
+  elif T is uint64: value.uint64 = data
+  elif T is int and sizeof(int) == sizeof(int32): value.int32 = data
+  elif T is int and sizeof(int) == sizeof(int64): value.int64 = data
+  elif T is uint and sizeof(uint) == sizeof(uint32): value.uint32 = data
+  elif T is uint and sizeof(uint) == sizeof(uint64): value.uint64 = data
+  elif T is float and sizeof(float) == sizeof(float32): value.float32 = data
+  elif T is float and sizeof(float) == sizeof(float64): value.float64 = data
+  elif T is TVec2[int32]: value.vec2i32 = data
+  elif T is TVec2[int64]: value.vec2i64 = data
+  elif T is TVec3[int32]: value.vec3i32 = data
+  elif T is TVec3[int64]: value.vec3i64 = data
+  elif T is TVec4[int32]: value.vec4i32 = data
+  elif T is TVec4[int64]: value.vec4i64 = data
+  elif T is TVec2[uint32]: value.vec2u32 = data
+  elif T is TVec2[uint64]: value.vec2u64 = data
+  elif T is TVec3[uint32]: value.vec3u32 = data
+  elif T is TVec3[uint64]: value.vec3u64 = data
+  elif T is TVec4[uint32]: value.vec4u32 = data
+  elif T is TVec4[uint64]: value.vec4u64 = data
+  elif T is TVec2[float32]: value.vec2f32 = data
+  elif T is TVec2[float64]: value.vec2f64 = data
+  elif T is TVec3[float32]: value.vec3f32 = data
+  elif T is TVec3[float64]: value.vec3f64 = data
+  elif T is TVec4[float32]: value.vec4f32 = data
+  elif T is TVec4[float64]: value.vec4f64 = data
+  elif T is TMat2[float32]: value.mat2f32 = data
+  elif T is TMat2[float64]: value.mat2f64 = data
+  elif T is TMat23[float32]: value.mat23f32 = data
+  elif T is TMat23[float64]: value.mat23f64 = data
+  elif T is TMat32[float32]: value.mat32f32 = data
+  elif T is TMat32[float64]: value.mat32f64 = data
+  elif T is TMat3[float32]: value.mat3f32 = data
+  elif T is TMat3[float64]: value.mat3f64 = data
+  elif T is TMat34[float32]: value.mat34f32 = data
+  elif T is TMat34[float64]: value.mat34f64 = data
+  elif T is TMat43[float32]: value.mat43f32 = data
+  elif T is TMat43[float64]: value.mat43f64 = data
+  elif T is TMat4[float32]: value.mat4f32 = data
+  elif T is TMat4[float64]: value.mat4f64 = data
+  else: {. error: "Virtual datatype has no values" .}
+
 func setValue*[T: GPUType|int|uint|float](value: var DataList, i: int, data: T) =
   assert i < value.len
-  when T is float32: value.float32[][i] = data
-  elif T is float64: value.float64[][i] = data
-  elif T is int8: value.int8[][i] = data
-  elif T is int16: value.int16[][i] = data
-  elif T is int32: value.int32[][i] = data
-  elif T is int64: value.int64[][i] = data
-  elif T is uint8: value.uint8[][i] = data
-  elif T is uint16: value.uint16[][i] = data
-  elif T is uint32: value.uint32[][i] = data
-  elif T is uint64: value.uint64[][i] = data
-  elif T is int and sizeof(int) == sizeof(int32): value.int32[][i] = data
-  elif T is int and sizeof(int) == sizeof(int64): value.int64[][i] = data
-  elif T is uint and sizeof(uint) == sizeof(uint32): value.uint32[][i] = data
-  elif T is uint and sizeof(uint) == sizeof(uint64): value.uint64[][i] = data
-  elif T is float and sizeof(float) == sizeof(float32): value.float32[][i] = data
-  elif T is float and sizeof(float) == sizeof(float64): value.float64[][i] = data
-  elif T is TVec2[int32]: value.vec2i32[][i] = data
-  elif T is TVec2[int64]: value.vec2i64[][i] = data
-  elif T is TVec3[int32]: value.vec3i32[][i] = data
-  elif T is TVec3[int64]: value.vec3i64[][i] = data
-  elif T is TVec4[int32]: value.vec4i32[][i] = data
-  elif T is TVec4[int64]: value.vec4i64[][i] = data
-  elif T is TVec2[uint32]: value.vec2u32[][i] = data
-  elif T is TVec2[uint64]: value.vec2u64[][i] = data
-  elif T is TVec3[uint32]: value.vec3u32[][i] = data
-  elif T is TVec3[uint64]: value.vec3u64[][i] = data
-  elif T is TVec4[uint32]: value.vec4u32[][i] = data
-  elif T is TVec4[uint64]: value.vec4u64[][i] = data
-  elif T is TVec2[float32]: value.vec2f32[][i] = data
-  elif T is TVec2[float64]: value.vec2f64[][i] = data
-  elif T is TVec3[float32]: value.vec3f32[][i] = data
-  elif T is TVec3[float64]: value.vec3f64[][i] = data
-  elif T is TVec4[float32]: value.vec4f32[][i] = data
-  elif T is TVec4[float64]: value.vec4f64[][i] = data
-  elif T is TMat2[float32]: value.mat2f32[][i] = data
-  elif T is TMat2[float64]: value.mat2f64[][i] = data
-  elif T is TMat23[float32]: value.mat23f32[][i] = data
-  elif T is TMat23[float64]: value.mat23f64[][i] = data
-  elif T is TMat32[float32]: value.mat32f32[][i] = data
-  elif T is TMat32[float64]: value.mat32f64[][i] = data
-  elif T is TMat3[float32]: value.mat3f32[][i] = data
-  elif T is TMat3[float64]: value.mat3f64[][i] = data
-  elif T is TMat34[float32]: value.mat34f32[][i] = data
-  elif T is TMat34[float64]: value.mat34f64[][i] = data
-  elif T is TMat43[float32]: value.mat43f32[][i] = data
-  elif T is TMat43[float64]: value.mat43f64[][i] = data
-  elif T is TMat4[float32]: value.mat4f32[][i] = data
-  elif T is TMat4[float64]: value.mat4f64[][i] = data
+  when T is float32: value.float32[i] = data
+  elif T is float64: value.float64[i] = data
+  elif T is int8: value.int8[i] = data
+  elif T is int16: value.int16[i] = data
+  elif T is int32: value.int32[i] = data
+  elif T is int64: value.int64[i] = data
+  elif T is uint8: value.uint8[i] = data
+  elif T is uint16: value.uint16[i] = data
+  elif T is uint32: value.uint32[i] = data
+  elif T is uint64: value.uint64[i] = data
+  elif T is int and sizeof(int) == sizeof(int32): value.int32[i] = data
+  elif T is int and sizeof(int) == sizeof(int64): value.int64[i] = data
+  elif T is uint and sizeof(uint) == sizeof(uint32): value.uint32[i] = data
+  elif T is uint and sizeof(uint) == sizeof(uint64): value.uint64[i] = data
+  elif T is float and sizeof(float) == sizeof(float32): value.float32[i] = data
+  elif T is float and sizeof(float) == sizeof(float64): value.float64[i] = data
+  elif T is TVec2[int32]: value.vec2i32[i] = data
+  elif T is TVec2[int64]: value.vec2i64[i] = data
+  elif T is TVec3[int32]: value.vec3i32[i] = data
+  elif T is TVec3[int64]: value.vec3i64[i] = data
+  elif T is TVec4[int32]: value.vec4i32[i] = data
+  elif T is TVec4[int64]: value.vec4i64[i] = data
+  elif T is TVec2[uint32]: value.vec2u32[i] = data
+  elif T is TVec2[uint64]: value.vec2u64[i] = data
+  elif T is TVec3[uint32]: value.vec3u32[i] = data
+  elif T is TVec3[uint64]: value.vec3u64[i] = data
+  elif T is TVec4[uint32]: value.vec4u32[i] = data
+  elif T is TVec4[uint64]: value.vec4u64[i] = data
+  elif T is TVec2[float32]: value.vec2f32[i] = data
+  elif T is TVec2[float64]: value.vec2f64[i] = data
+  elif T is TVec3[float32]: value.vec3f32[i] = data
+  elif T is TVec3[float64]: value.vec3f64[i] = data
+  elif T is TVec4[float32]: value.vec4f32[i] = data
+  elif T is TVec4[float64]: value.vec4f64[i] = data
+  elif T is TMat2[float32]: value.mat2f32[i] = data
+  elif T is TMat2[float64]: value.mat2f64[i] = data
+  elif T is TMat23[float32]: value.mat23f32[i] = data
+  elif T is TMat23[float64]: value.mat23f64[i] = data
+  elif T is TMat32[float32]: value.mat32f32[i] = data
+  elif T is TMat32[float64]: value.mat32f64[i] = data
+  elif T is TMat3[float32]: value.mat3f32[i] = data
+  elif T is TMat3[float64]: value.mat3f64[i] = data
+  elif T is TMat34[float32]: value.mat34f32[i] = data
+  elif T is TMat34[float64]: value.mat34f64[i] = data
+  elif T is TMat43[float32]: value.mat43f32[i] = data
+  elif T is TMat43[float64]: value.mat43f64[i] = data
+  elif T is TMat4[float32]: value.mat4f32[i] = data
+  elif T is TMat4[float64]: value.mat4f64[i] = data
   else: {. error: "Virtual datatype has no values" .}
 
 const TYPEMAP = {
--- a/src/semicongine/core/utils.nim	Tue Aug 29 00:01:13 2023 +0700
+++ b/src/semicongine/core/utils.nim	Sat Sep 02 23:51:02 2023 +0700
@@ -8,8 +8,8 @@
       result = join(str[0 ..< i])
       break
 
-func toCPointer*[T](list: var seq[T]): ptr T =
-  if list.len > 0: addr list[0] else: nil
+func toCPointer*[T](list: seq[T]): ptr T =
+  if list.len > 0: addr(list[0]) else: nil
 
 proc staticExecChecked*(command: string, input = ""): string {.compileTime.} =
   let (output, exitcode) = gorgeEx(
--- a/src/semicongine/engine.nim	Tue Aug 29 00:01:13 2023 +0700
+++ b/src/semicongine/engine.nim	Sat Sep 02 23:51:02 2023 +0700
@@ -15,6 +15,7 @@
 import ./renderer
 import ./events
 import ./audio
+import ./text
 
 type
   EngineState* = enum
@@ -105,12 +106,19 @@
 proc initRenderer*(engine: var Engine, shaders: Table[string, ShaderConfiguration], clearColor=Vec4f([0.8'f32, 0.8'f32, 0.8'f32, 1'f32])) =
 
   assert not engine.renderer.isSome
-  engine.renderer = some(engine.device.initRenderer(shaders=shaders, clearColor=clearColor))
+  var materialShaderMap = shaders
+  materialShaderMap[TEXT_MATERIAL] = TEXT_SHADER
+  engine.renderer = some(engine.device.initRenderer(shaders=materialShaderMap, clearColor=clearColor))
+
+proc initRenderer*(engine: var Engine, clearColor=Vec4f([0.8'f32, 0.8'f32, 0.8'f32, 1'f32])) =
+  var materialShaderMap: Table[string, ShaderConfiguration]
+  engine.initRenderer(materialShaderMap, clearColor)
 
 proc addScene*(engine: var Engine, scene: var Scene) =
   assert engine.renderer.isSome
   engine.renderer.get.setupDrawableBuffers(scene)
   engine.renderer.get.updateMeshData(scene, forceAll=true)
+  engine.renderer.get.updateUniformData(scene, forceAll=true)
 
 proc renderScene*(engine: var Engine, scene: var Scene) =
   assert engine.state == Running
--- a/src/semicongine/mesh.nim	Tue Aug 29 00:01:13 2023 +0700
+++ b/src/semicongine/mesh.nim	Sat Sep 02 23:51:02 2023 +0700
@@ -31,13 +31,11 @@
     instanceData: Table[string, DataList]
     dirtyAttributes: seq[string]
   Material* = object
-    materialType*: string
     name*: string
-    constants*: Table[string, DataValue]
+    constants*: Table[string, DataList]
     textures*: Table[string, Texture]
 
 let EMPTY_MATERIAL = Material(
-  materialType: "EMPTY MATERIAL",
   name: "empty material"
 )
 
@@ -214,7 +212,7 @@
     of Small: rawData(mesh.smallIndices)
     of Big: rawData(mesh.bigIndices)
 
-func getRawData*(mesh: Mesh, attribute: string): (pointer, int) =
+func getRawData*(mesh: var Mesh, attribute: string): (pointer, int) =
   if mesh.vertexData.contains(attribute):
     mesh.vertexData[attribute].getRawData()
   elif mesh.instanceData.contains(attribute):
@@ -222,7 +220,7 @@
   else:
     raise newException(Exception, &"Attribute {attribute} is not defined for mesh {mesh}")
 
-proc getAttribute*[T: GPUType|int|uint|float](mesh: Mesh, attribute: string): ref seq[T] =
+proc getAttribute*[T: GPUType|int|uint|float](mesh: Mesh, attribute: string): seq[T] =
   if mesh.vertexData.contains(attribute):
     getValues[T](mesh.vertexData[attribute])
   elif mesh.instanceData.contains(attribute):
@@ -294,11 +292,11 @@
 
 proc transform*[T: GPUType](mesh: Mesh, attribute: string, transform: Mat4) =
   if mesh.vertexData.contains(attribute):
-    for v in getValues[T](mesh.vertexData[attribute])[].mitems:
-      v = transform * v
+    for i in 0 ..< mesh.vertexData[attribute].len:
+      setValue(mesh.vertexData[attribute], i, transform * getValue[T](mesh.vertexData[attribute], i))
   elif mesh.instanceData.contains(attribute):
-    for v in getValues[T](mesh.instanceData[attribute])[].mitems:
-      v = transform * v
+    for i in 0 ..< mesh.instanceData[attribute].len:
+      setValue(mesh.instanceData[attribute], i, transform * getValue[T](mesh.vertexData[attribute], i))
   else:
     raise newException(Exception, &"Attribute {attribute} is not defined for mesh {mesh}")
 
@@ -351,5 +349,5 @@
   result.initVertexAttribute("color", col)
 
 func getCollisionPoints*(mesh: Mesh, positionAttribute="position"): seq[Vec3f] =
-  for p in getAttribute[Vec3f](mesh, positionAttribute)[]:
+  for p in getAttribute[Vec3f](mesh, positionAttribute):
     result.add mesh.transform * p
--- a/src/semicongine/renderer.nim	Tue Aug 29 00:01:13 2023 +0700
+++ b/src/semicongine/renderer.nim	Sat Sep 02 23:51:02 2023 +0700
@@ -20,8 +20,8 @@
 import ./scene
 import ./mesh
 
-const MATERIALINDEXATTRIBUTE = "materialIndex"
 const TRANSFORMATTRIBUTE = "transform"
+const VERTEX_ATTRIB_ALIGNMENT = 4 # used for buffer alignment
 
 type
   SceneData = object
@@ -43,14 +43,8 @@
     scenedata: Table[Scene, SceneData]
     emptyTexture: VulkanTexture
 
-func usesMaterialType(scene: Scene, materialType: string): bool =
-  return scene.meshes.anyIt(it.material.materialType == materialType)
-
-func getPipelineForMaterialtype(renderer: Renderer, materialType: string): Option[Pipeline] =
-  for i in 0 ..< renderer.renderPass.subpasses.len:
-    for pipelineMaterialType, pipeline in renderer.renderPass.subpasses[i].pipelines.pairs:
-      if pipelineMaterialType == materialType:
-        return some(pipeline)
+func usesMaterial(scene: Scene, materialName: string): bool =
+  return scene.meshes.anyIt(it.material.name == materialName)
 
 proc initRenderer*(device: Device, shaders: Table[string, ShaderConfiguration], clearColor=Vec4f([0.8'f32, 0.8'f32, 0.8'f32, 1'f32])): Renderer =
   assert device.vk.valid
@@ -69,82 +63,85 @@
 func inputs(renderer: Renderer, scene: Scene): seq[ShaderAttribute] =
   var found: Table[string, ShaderAttribute]
   for i in 0 ..< renderer.renderPass.subpasses.len:
-    for materialType, pipeline in renderer.renderPass.subpasses[i].pipelines.pairs:
-      if scene.usesMaterialType(materialType):
+    for materialName, pipeline in renderer.renderPass.subpasses[i].pipelines.pairs:
+      if scene.usesMaterial(materialName):
         for input in pipeline.inputs:
           if found.contains(input.name):
-            assert input == found[input.name]
+            assert input == found[input.name], &"{input} != {found[input.name]}"
           else:
             result.add input
             found[input.name] = input
 
 func samplers(renderer: Renderer, scene: Scene): seq[ShaderAttribute] =
   for i in 0 ..< renderer.renderPass.subpasses.len:
-    for materialType, pipeline in renderer.renderPass.subpasses[i].pipelines.pairs:
-      if scene.usesMaterialType(materialType):
+    for materialName, pipeline in renderer.renderPass.subpasses[i].pipelines.pairs:
+      if scene.usesMaterial(materialName):
         result.add pipeline.samplers
 
 func materialCompatibleWithPipeline(scene: Scene, material: Material, pipeline: Pipeline): (bool, string) =
   for uniform in pipeline.uniforms:
     if scene.shaderGlobals.contains(uniform.name):
       if scene.shaderGlobals[uniform.name].theType != uniform.theType:
-        return (false, &"shader uniform needs type {uniform.theType} but scene global is of type {scene.shaderGlobals[uniform.name].theType}")
+        return (true, &"shader uniform needs type {uniform.theType} but scene global is of type {scene.shaderGlobals[uniform.name].theType}")
     else:
-      var foundMatch = false
+      var foundMatch = true
       for name, constant in material.constants.pairs:
         if name == uniform.name and constant.theType == uniform.theType:
           foundMatch = true
           break
       if not foundMatch:
-        return (false, "shader uniform '{uniform.name}' was not found in scene globals or scene materials")
+        return (true, &"shader uniform '{uniform.name}' was not found in scene globals or scene materials")
   for sampler in pipeline.samplers:
-    var foundMatch = false
+    var foundMatch = true
     for name, value in material.textures:
       if name == sampler.name:
         foundMatch = true
         break
     if not foundMatch:
-      return (false, "Required texture for shader sampler '{sampler.name}' was not found in scene materials")
+      return (true, &"Required texture for shader sampler '{sampler.name}' was not found in scene materials")
 
-  return (true, "")
+  return (false, "")
 
 func meshCompatibleWithPipeline(scene: Scene, mesh: Mesh, pipeline: Pipeline): (bool, string) =
   for input in pipeline.inputs:
+    if input.name == TRANSFORMATTRIBUTE: # will be populated automatically
+      continue
     if not (input.name in mesh.attributes):
-      return (false, &"Shader input '{input.name}' is not available for mesh '{mesh}'")
+      return (true, &"Shader input '{input.name}' is not available for mesh '{mesh}'")
     if input.theType != mesh.attributeType(input.name):
-      return (false, &"Shader input '{input.name}' expects type {input.theType}, but mesh '{mesh}' has {mesh.attributeType(input.name)}")
+      return (true, &"Shader input '{input.name}' expects type {input.theType}, but mesh '{mesh}' has {mesh.attributeType(input.name)}")
     if input.perInstance != mesh.instanceAttributes.contains(input.name):
-      return (false, &"Shader input '{input.name}' expects to be per instance, but mesh '{mesh}' has is not as instance attribute")
+      return (true, &"Shader input '{input.name}' expects to be per instance, but mesh '{mesh}' has is not as instance attribute")
 
   return materialCompatibleWithPipeline(scene, mesh.material, pipeline)
 
 func checkSceneIntegrity(renderer: Renderer, scene: Scene) =
+  if scene.meshes.len == 0:
+    return
+
   var foundRenderableObject = false
   var shaderTypes: seq[string]
   for i in 0 ..< renderer.renderPass.subpasses.len:
-    for materialType, pipeline in renderer.renderPass.subpasses[i].pipelines.pairs:
-      shaderTypes.add materialType
+    for materialName, pipeline in renderer.renderPass.subpasses[i].pipelines.pairs:
+      shaderTypes.add materialName
       for mesh in scene.meshes:
-        if mesh.material.materialType == materialType:
+        if mesh.material.name == materialName:
           foundRenderableObject = true
           let (error, message) = scene.meshCompatibleWithPipeline(mesh, pipeline)
           if error:
-            raise newException(Exception, &"Mesh '{mesh}' not compatible with assigned pipeline ({materialType}) because: {message}")
+            raise newException(Exception, &"Mesh '{mesh}' not compatible with assigned pipeline ({materialName}) because: {message}")
 
   if not foundRenderableObject:
     var materialTypes: seq[string]
     for mesh in scene.meshes:
-      if not materialTypes.contains(mesh.material.materialType):
-          materialTypes.add mesh.material.materialType
-    raise newException(Exception, &"Scene {scene.name} has been added but materials are not compatible with any registered shader: Materials in scene: {materialTypes}, registered shader-materialtypes: {shaderTypes}")
+      if not materialTypes.contains(mesh.material.name):
+          materialTypes.add mesh.material.name
+    raise newException(Exception, &"Scene '{scene.name}' has been added but materials are not compatible with any registered shader: Materials in scene: {materialTypes}, registered shader-materialtypes: {shaderTypes}")
 
 proc setupDrawableBuffers*(renderer: var Renderer, scene: var Scene) =
   assert not (scene in renderer.scenedata)
   renderer.checkSceneIntegrity(scene)
 
-  const VERTEX_ATTRIB_ALIGNMENT = 4 # used for buffer alignment
-
   let
     inputs = renderer.inputs(scene)
     samplers = renderer.samplers(scene)
@@ -163,11 +160,6 @@
     for inputAttr in inputs:
       if inputAttr.name == TRANSFORMATTRIBUTE:
         mesh.initInstanceAttribute(inputAttr.name, inputAttr.thetype)
-      elif inputAttr.name == MATERIALINDEXATTRIBUTE:
-        let matIndex = scenedata.materials.find(mesh.material)
-        if matIndex < 0:
-          raise newException(Exception, &"Required material '{mesh.material}' not available in scene (available are: {scenedata.materials})")
-        mesh.initInstanceAttribute(inputAttr.name, uint16(matIndex))
       elif not mesh.attributes.contains(inputAttr.name):
         warn(&"Mesh is missing data for shader attribute {inputAttr.name}, auto-filling with empty values")
         if inputAttr.perInstance:
@@ -222,13 +214,13 @@
         preferVRAM=true,
       )
 
-  # calculate offset of each attribute of all meshes
+  # calculate offset of each attribute for all meshes
   var perLocationOffsets: Table[MemoryPerformanceHint, int]
   var indexBufferOffset = 0
   for hint in MemoryPerformanceHint:
     perLocationOffsets[hint] = 0
 
-  for (meshIndex, mesh) in enumerate(scene.meshes):
+  for (meshIndex, mesh) in enumerate(scene.meshes.mitems):
     for attribute in inputs:
       scenedata.vertexBufferOffsets[(meshIndex, attribute.name)] = perLocationOffsets[attribute.memoryPerformanceHint]
       let size = mesh.getRawData(attribute.name)[1]
@@ -239,8 +231,8 @@
     # fill offsets per pipeline (as sequence corresponds to shader input binding)
     var offsets: Table[VkPipeline, seq[(string, MemoryPerformanceHint, int)]]
     for subpass_i in 0 ..< renderer.renderPass.subpasses.len:
-      for materialType, pipeline in renderer.renderPass.subpasses[subpass_i].pipelines.pairs:
-        if scene.usesMaterialType(materialType):
+      for materialName, pipeline in renderer.renderPass.subpasses[subpass_i].pipelines.pairs:
+        if scene.usesMaterial(materialName):
           offsets[pipeline.vk] = newSeq[(string, MemoryPerformanceHint, int)]()
           for attribute in pipeline.inputs:
             offsets[pipeline.vk].add (attribute.name, attribute.memoryPerformanceHint, scenedata.vertexBufferOffsets[(meshIndex, attribute.name)])
@@ -271,8 +263,8 @@
 
   # setup uniforms and samplers
   for subpass_i in 0 ..< renderer.renderPass.subpasses.len:
-    for materialType, pipeline in renderer.renderPass.subpasses[subpass_i].pipelines.pairs:
-      if scene.usesMaterialType(materialType):
+    for materialName, pipeline in renderer.renderPass.subpasses[subpass_i].pipelines.pairs:
+      if scene.usesMaterial(materialName):
         var uniformBufferSize = 0
         for uniform in pipeline.uniforms:
           uniformBufferSize += uniform.size
@@ -307,13 +299,14 @@
 
   renderer.scenedata[scene] = scenedata
 
-proc refreshMeshAttributeData(renderer: Renderer, scene: Scene, drawable: Drawable, meshIndex: int, attribute: string) =
+proc refreshMeshAttributeData(renderer: Renderer, scene: var Scene, drawable: Drawable, meshIndex: int, attribute: string) =
   debug &"Refreshing data on mesh {scene.meshes[meshIndex]} for {attribute}"
   # ignore attributes that are not used in this shader
   if not (attribute in renderer.scenedata[scene].attributeLocation):
     return
-  var (pdata, size) = scene.meshes[meshIndex].getRawData(attribute)
+  let (pdata, size) = scene.meshes[meshIndex].getRawData(attribute)
   let memoryPerformanceHint = renderer.scenedata[scene].attributeLocation[attribute]
+
   renderer.scenedata[scene].vertexBuffers[memoryPerformanceHint].setData(pdata, size, renderer.scenedata[scene].vertexBufferOffsets[(meshIndex, attribute)])
 
 proc updateMeshData*(renderer: var Renderer, scene: var Scene, forceAll=false) =
@@ -328,25 +321,33 @@
       debug &"Update mesh attribute {attribute}"
     scene.meshes[meshIndex].clearDirtyAttributes()
 
-proc updateUniformData*(renderer: var Renderer, scene: var Scene) =
+# TODO: only update if uniform values changed
+proc updateUniformData*(renderer: var Renderer, scene: var Scene, forceAll=true) =
   assert scene in renderer.scenedata
 
+  # loop over all used shaders/pipelines
   for i in 0 ..< renderer.renderPass.subpasses.len:
-    for materialType, pipeline in renderer.renderPass.subpasses[i].pipelines.pairs:
-      if scene.usesMaterialType(materialType) and renderer.scenedata[scene].uniformBuffers.hasKey(pipeline.vk) and renderer.scenedata[scene].uniformBuffers[pipeline.vk].len != 0:
+    for materialName, pipeline in renderer.renderPass.subpasses[i].pipelines.pairs:
+      if scene.usesMaterial(materialName) and renderer.scenedata[scene].uniformBuffers.hasKey(pipeline.vk) and renderer.scenedata[scene].uniformBuffers[pipeline.vk].len != 0:
         assert renderer.scenedata[scene].uniformBuffers[pipeline.vk][renderer.swapchain.currentInFlight].vk.valid
         var offset = 0
+        # loop over all uniforms of the shader
         for uniform in pipeline.uniforms:
-          var value = newDataList(thetype=uniform.thetype)
+          var foundValue = false
+          var value: DataList
           if scene.shaderGlobals.hasKey(uniform.name):
-            assert scene.shaderGlobals[uniform.name].thetype == value.thetype
+            assert scene.shaderGlobals[uniform.name].thetype == uniform.thetype
             value = scene.shaderGlobals[uniform.name]
+            foundValue = true
           else:
             for mat in renderer.scenedata[scene].materials:
-                for name, materialConstant in mat.constants.pairs:
-                  if uniform.name == name:
-                    value.appendValue(materialConstant)
-          if value.len == 0:
+              for name, materialConstant in mat.constants.pairs:
+                if uniform.name == name:
+                  value = materialConstant
+                  foundValue = true
+                  break
+              if foundValue: break
+          if not foundValue:
             raise newException(Exception, &"Uniform '{uniform.name}' not found in scene shaderGlobals or materials")
           debug &"Update uniform {uniform.name}"
           let (pdata, size) = value.getRawData()
@@ -378,14 +379,14 @@
   debug "  Index buffer: ", renderer.scenedata[scene].indexBuffer
 
   for i in 0 ..< renderer.renderPass.subpasses.len:
-    for materialType, pipeline in renderer.renderPass.subpasses[i].pipelines.pairs:
-      if scene.usesMaterialType(materialType):
-        debug &"Start pipeline for {materialType}"
+    for materialName, pipeline in renderer.renderPass.subpasses[i].pipelines.pairs:
+      if scene.usesMaterial(materialName):
+        debug &"Start pipeline for {materialName}"
         commandBuffer.vkCmdBindPipeline(renderer.renderPass.subpasses[i].pipelineBindPoint, pipeline.vk)
         commandBuffer.vkCmdBindDescriptorSets(renderer.renderPass.subpasses[i].pipelineBindPoint, pipeline.layout, 0, 1, addr(renderer.scenedata[scene].descriptorSets[pipeline.vk][renderer.swapchain.currentInFlight].vk), 0, nil)
 
         for (drawable, meshIndex) in renderer.scenedata[scene].drawables:
-          if scene.meshes[meshIndex].material.materialType == materialType:
+          if scene.meshes[meshIndex].material.name == materialName:
             drawable.draw(commandBuffer, vertexBuffers=renderer.scenedata[scene].vertexBuffers, indexBuffer=renderer.scenedata[scene].indexBuffer, pipeline.vk)
 
     if i < renderer.renderPass.subpasses.len - 1:
--- a/src/semicongine/resources/mesh.nim	Tue Aug 29 00:01:13 2023 +0700
+++ b/src/semicongine/resources/mesh.nim	Sat Sep 02 23:51:02 2023 +0700
@@ -48,7 +48,7 @@
     10497: VK_SAMPLER_ADDRESS_MODE_REPEAT
   }.toTable
 
-func getGPUType(accessor: JsonNode): DataType =
+proc getGPUType(accessor: JsonNode): DataType =
   # TODO: no full support for all datatypes that glTF may provide
   # semicongine/core/gpu_data should maybe generated with macros to allow for all combinations
   let componentType = ACCESSOR_TYPE_MAP[accessor["componentType"].getInt()]
@@ -157,57 +157,57 @@
   let pbr = materialNode["pbrMetallicRoughness"]
 
   # color
-  result.constants["baseColorFactor"] = DataValue(thetype: Vec4F32)
+  result.constants["baseColorFactor"] = newDataList(thetype=Vec4F32)
   if pbr.hasKey("baseColorFactor"):
-    setValue(result.constants["baseColorFactor"], newVec4f(
+    setValue(result.constants["baseColorFactor"], @[newVec4f(
       pbr["baseColorFactor"][0].getFloat(),
       pbr["baseColorFactor"][1].getFloat(),
       pbr["baseColorFactor"][2].getFloat(),
       pbr["baseColorFactor"][3].getFloat(),
-    ))
+    )])
   else:
-    setValue(result.constants["baseColorFactor"], newVec4f(1, 1, 1, 1))
+    setValue(result.constants["baseColorFactor"], @[newVec4f(1, 1, 1, 1)])
 
   # pbr material constants
   for factor in ["metallicFactor", "roughnessFactor"]:
-    result.constants[factor] = DataValue(thetype: Float32)
+    result.constants[factor] = newDataList(thetype=Float32)
     if pbr.hasKey(factor):
-      setValue(result.constants[factor], float32(pbr[factor].getFloat()))
+      setValue(result.constants[factor], @[float32(pbr[factor].getFloat())])
     else:
-      setValue(result.constants[factor], 0.5'f32)
+      setValue(result.constants[factor], @[0.5'f32])
 
   # pbr material textures
   for texture in ["baseColorTexture", "metallicRoughnessTexture"]:
     if pbr.hasKey(texture):
       result.textures[texture] = loadTexture(root, pbr[texture]["index"].getInt(), mainBuffer)
-      result.constants[texture & "Index"] = DataValue(thetype: UInt8)
-      setValue(result.constants[texture & "Index"], pbr[texture].getOrDefault("texCoord").getInt(0).uint8)
+      result.constants[texture & "Index"] = newDataList(thetype=UInt8)
+      setValue(result.constants[texture & "Index"], @[pbr[texture].getOrDefault("texCoord").getInt(0).uint8])
     else:
       result.textures[texture] = EMPTYTEXTURE
-      result.constants[texture & "Index"] = DataValue(thetype: UInt8)
-      setValue(result.constants[texture & "Index"], 0'u8)
+      result.constants[texture & "Index"] = newDataList(thetype=UInt8)
+      setValue(result.constants[texture & "Index"], @[0'u8])
 
   # generic material textures
   for texture in ["normalTexture", "occlusionTexture", "emissiveTexture"]:
     if materialNode.hasKey(texture):
       result.textures[texture] = loadTexture(root, materialNode[texture]["index"].getInt(), mainBuffer)
-      result.constants[texture & "Index"] = DataValue(thetype: UInt8)
-      setValue(result.constants[texture & "Index"], materialNode[texture].getOrDefault("texCoord").getInt(0).uint8)
+      result.constants[texture & "Index"] = newDataList(thetype=UInt8)
+      setValue(result.constants[texture & "Index"], @[materialNode[texture].getOrDefault("texCoord").getInt(0).uint8])
     else:
       result.textures[texture] = EMPTYTEXTURE
-      result.constants[texture & "Index"] = DataValue(thetype: UInt8)
-      setValue(result.constants[texture & "Index"], 0'u8)
+      result.constants[texture & "Index"] = newDataList(thetype=UInt8)
+      setValue(result.constants[texture & "Index"], @[0'u8])
 
   # emissiv color
-  result.constants["emissiveFactor"] = DataValue(thetype: Vec3F32)
+  result.constants["emissiveFactor"] = newDataList(thetype=Vec3F32)
   if materialNode.hasKey("emissiveFactor"):
-    setValue(result.constants["emissiveFactor"], newVec3f(
+    setValue(result.constants["emissiveFactor"], @[newVec3f(
       materialNode["emissiveFactor"][0].getFloat(),
       materialNode["emissiveFactor"][1].getFloat(),
       materialNode["emissiveFactor"][2].getFloat(),
-    ))
+    )])
   else:
-    setValue(result.constants["emissiveFactor"], newVec3f(1'f32, 1'f32, 1'f32))
+    setValue(result.constants["emissiveFactor"], @[newVec3f(1'f32, 1'f32, 1'f32)])
 
 
 proc addPrimitive(mesh: var Mesh, root: JsonNode, primitiveNode: JsonNode, mainBuffer: seq[uint8]) =
@@ -236,14 +236,14 @@
     var tri: seq[int]
     case data.thetype
       of UInt16:
-        for entry in getValues[uint16](data)[]:
+        for entry in getValues[uint16](data):
           tri.add int(entry) + baseIndex
           if tri.len == 3:
             # FYI gltf uses counter-clockwise indexing
             mesh.appendIndicesData(tri[0], tri[2], tri[1])
             tri.setLen(0)
       of UInt32:
-        for entry in getValues[uint32](data)[]:
+        for entry in getValues[uint32](data):
           tri.add int(entry)
           if tri.len == 3:
             # FYI gltf uses counter-clockwise indexing
--- a/src/semicongine/text.nim	Tue Aug 29 00:01:13 2023 +0700
+++ b/src/semicongine/text.nim	Sat Sep 02 23:51:02 2023 +0700
@@ -1,10 +1,9 @@
 import std/tables
 import std/unicode
 
+import ./core
 import ./mesh
-import ./core/vector
-import ./core/matrix
-import ./core/fonttypes
+import ./vulkan/shader
 
 type
   TextAlignment = enum
@@ -19,6 +18,26 @@
     font*: Font
     mesh*: Mesh
 
+const
+  TEXT_MATERIAL* = "default-text"
+  TEXT_SHADER* = createShaderConfiguration(
+    inputs=[
+      attr[Mat4]("transform", memoryPerformanceHint=PreferFastWrite),
+      attr[Vec3f]("position", memoryPerformanceHint=PreferFastWrite),
+      attr[Vec2f]("uv", memoryPerformanceHint=PreferFastWrite),
+    ],
+    intermediates=[attr[Vec2f]("uvFrag")],
+    outputs=[attr[Vec4f]("color")],
+    samplers=[attr[Sampler2DType]("fontAtlas")],
+    uniforms=[
+      attr[Mat4]("perspective"),
+    ],
+    vertexCode="""gl_Position = vec4(position, 1.0) * (transform * Uniforms.perspective); uvFrag = uv;""",
+    # vertexCode="""gl_Position = vec4(position, 1.0);""",
+    fragmentCode="""color = texture(fontAtlas, uvFrag);""",
+    # fragmentCode="""color = vec4(1, 0, 0, 1);""",
+  )
+
 proc updateMesh(textbox: var Textbox) =
 
   # pre-calculate text-width
@@ -69,7 +88,7 @@
   textbox.text = text
   textbox.updateMesh()
 
-proc newTextbox*(maxLen: int, font: Font, text=toRunes("")): Textbox =
+proc initTextbox*(maxLen: int, font: Font, text=toRunes("")): Textbox =
   var
     positions = newSeq[Vec3f](int(maxLen * 4))
     indices: seq[array[3, uint16]]
@@ -83,6 +102,11 @@
 
   result = Textbox(maxLen: maxLen, text: text, font: font, dirty: true)
   result.mesh = newMesh(positions = positions, indices = indices, uvs = uvs)
+  result.mesh.material = Material(
+    name: TEXT_MATERIAL,
+    # constants: {"glyphCoordinates": }.toTable,
+    textures: {"fontAtlas": font.fontAtlas}.toTable,
+  )
 
   # wrap the text mesh in a new entity to preserve the font-scaling
   result.mesh.transform = scale(1 / font.resolution, 1 / font.resolution)
--- a/src/semicongine/vulkan/drawable.nim	Tue Aug 29 00:01:13 2023 +0700
+++ b/src/semicongine/vulkan/drawable.nim	Sat Sep 02 23:51:02 2023 +0700
@@ -29,10 +29,14 @@
     var buffers: seq[VkBuffer]
     var offsets: seq[VkDeviceSize]
 
+
     for (name, performanceHint, offset) in drawable.bufferOffsets[pipeline]:
       buffers.add vertexBuffers[performanceHint].vk
       offsets.add VkDeviceSize(offset)
 
+      # echo "####### ", name
+      # echo "####### ", (cast[ptr array[4, Vec3f]](vertexBuffers[performanceHint].memory.data))[]
+
     commandBuffer.vkCmdBindVertexBuffers(
       firstBinding=0'u32,
       bindingCount=uint32(buffers.len),
--- a/tests/test_font.nim	Tue Aug 29 00:01:13 2023 +0700
+++ b/tests/test_font.nim	Sat Sep 02 23:51:02 2023 +0700
@@ -4,48 +4,13 @@
 import semicongine
 
 proc main() =
-  var sampler = DefaultSampler()
-  sampler.magnification = VK_FILTER_NEAREST
-  sampler.minification = VK_FILTER_NEAREST
   var font = loadFont("DejaVuSans.ttf", color=newVec4f(1, 0.5, 0.5, 1), resolution=20)
-
-  var scene = newScene("main", root=newEntity("rect"))
-
-  var flag = rect()
-  flag.setInstanceData("material", @[0'u8])
-  # scene.root.add flag
-  scene.addMaterial Material(name: "material", textures: {"textures": Texture(image: loadImage("flag.png"), sampler: sampler)}.toTable)
-
-  var textbox = newTextbox(32, font, "".toRunes)
-  scene.addMaterial Material(name: "fontMaterial", textures: {"textures": font.fontAtlas}.toTable)
-  textbox.mesh.setInstanceData("material", @[1'u8])
-  textbox.transform = scale3d(0.1, 0.1)
-  scene.root.add textbox
-
-  const
-    vertexInput = @[
-      attr[Mat4]("transform", memoryPerformanceHint=PreferFastRead, perInstance=true),
-      attr[Vec3f]("position", memoryPerformanceHint=PreferFastRead),
-      attr[Vec2f]("uv", memoryPerformanceHint=PreferFastRead),
-      attr[uint8]("material", memoryPerformanceHint=PreferFastRead, perInstance=true),
-    ]
-    intermediate = @[attr[Vec2f]("uvout"), attr[uint8]("materialId", noInterpolation=true)]
-    samplers = @[attr[Sampler2DType]("textures", arrayCount=2)]
-    uniforms = @[attr[Mat4]("perspective")]
-    fragOutput = @[attr[Vec4f]("color")]
-    (vertexCode, fragmentCode) = compileVertexFragmentShaderSet(
-      inputs=vertexInput,
-      intermediate=intermediate,
-      outputs=fragOutput,
-      samplers=samplers,
-      uniforms=uniforms,
-      vertexCode="""gl_Position = vec4(position, 1.0) * (transform * Uniforms.perspective); uvout = uv; materialId = material;""",
-      fragmentCode="""color = texture(textures[materialId], uvout);""",
-    )
-
+  
+  var textbox = initTextbox(32, font, "".toRunes)
+  var scene = Scene(name: "main", meshes: @[textbox.mesh])
   var engine = initEngine("Test fonts")
-  engine.setRenderer(engine.gpuDevice.simpleForwardRenderPass(vertexCode, fragmentCode))
-  engine.addScene(scene, vertexInput, samplers, materialIndexAttribute="")
+  engine.initRenderer()
+  engine.addScene(scene)
   scene.addShaderGlobal("perspective", Unit4F32)
 
   while engine.updateInputs() == Running and not engine.keyIsDown(Escape):
--- a/tests/test_vulkan_wrapper.nim	Tue Aug 29 00:01:13 2023 +0700
+++ b/tests/test_vulkan_wrapper.nim	Sat Sep 02 23:51:02 2023 +0700
@@ -13,7 +13,6 @@
   (R, W) = ([255'u8, 0'u8, 0'u8, 255'u8], [255'u8, 255'u8, 255'u8, 255'u8])
   mat = Material(
     name: "mat",
-    materialType: "textures_material",
     textures: {
       "my_little_texture": Texture(image: Image(width: 5, height: 5, imagedata: @[
       R, R, R, R, R,
@@ -26,7 +25,6 @@
   )
   mat2 = Material(
     name: "mat2",
-    materialType: "textures_material",
     textures: {
       "my_little_texture": Texture(image: Image(width: 5, height: 5, imagedata: @[
       R, W, R, W, R,
@@ -39,9 +37,8 @@
   )
   mat3 = Material(
     name: "mat3",
-    materialType: "plain",
     constants: {
-      "color": toGPUValue(newVec4f(0, 1, 0, 1))
+      "color": toGPUValue(@[newVec4f(0, 1, 0, 1)])
     }.toTable
   )
 
@@ -166,19 +163,17 @@
         attr[Vec3f]("position", memoryPerformanceHint=PreferFastRead),
         attr[Vec4f]("color", memoryPerformanceHint=PreferFastWrite),
         attr[Mat4]("transform", perInstance=true),
-        attr[uint16]("materialIndex", perInstance=true),
       ],
       intermediates=[
         attr[Vec4f]("outcolor"),
-        attr[uint16]("materialIndexOut", noInterpolation=true),
       ],
       outputs=[attr[Vec4f]("color")],
       uniforms=[attr[float32]("time")],
       samplers=[
-        attr[Sampler2DType]("my_little_texture", arrayCount=2)
+        attr[Sampler2DType]("my_little_texture")
       ],
-      vertexCode="""gl_Position = vec4(position, 1.0) * transform; outcolor = color; materialIndexOut = materialIndex;""",
-      fragmentCode="color = texture(my_little_texture[materialIndexOut], outcolor.xy) * 0.5 + outcolor * 0.5;",
+      vertexCode="""gl_Position = vec4(position, 1.0) * transform; outcolor = color;""",
+      fragmentCode="color = texture(my_little_texture, outcolor.xy) * 0.5 + outcolor * 0.5;",
     )
     shaderConfiguration2 = createShaderConfiguration(
       inputs=[
@@ -187,13 +182,14 @@
       ],
       intermediates=[attr[Vec4f]("outcolor")],
       outputs=[attr[Vec4f]("color")],
-      uniforms=[attr[Vec4f]("color")],
-      vertexCode="""gl_Position = vec4(position, 1.0) * transform; outcolor = Uniforms.color;""",
+      uniforms=[attr[Vec4f]("color", arrayCount=1)],
+      vertexCode="""gl_Position = vec4(position, 1.0) * transform; outcolor = Uniforms.color[0];""",
       fragmentCode="color = outcolor;",
     )
   engine.initRenderer({
-    "textures_material": shaderConfiguration1,
-    "plain": shaderConfiguration2,
+    "mat": shaderConfiguration1,
+    "mat2": shaderConfiguration1,
+    "mat3": shaderConfiguration2,
   }.toTable)
 
   # INIT SCENES