view src/semicongine/core/gpu_data.nim @ 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 27aaf43e18b4
line wrap: on
line source

import std/strformat
import std/tables
import std/hashes

import ./vulkanapi
import ./vector
import ./matrix
import ./utils

type
  Sampler2DType* = object
  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] | Sampler2DType
  DataType* = enum
    Float32
    Float64
    Int8
    Int16
    Int32
    Int64
    UInt8
    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
    Sampler2D
  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]
    of Sampler2D: discard
  DataList* = object
    len*: int
    case theType*: DataType
    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
  ShaderAttribute* = object
    name*: string
    theType*: DataType
    arrayCount*: int
    perInstance*: bool
    noInterpolation: bool
    memoryPerformanceHint*: MemoryPerformanceHint

func `==`*(a, b: DataList | DataValue): bool =
  if a.theType != b.theType:
    return false
  case a.theType
    of Float32: return a.float32 == b.float32
    of Float64: return a.float64 == b.float64
    of Int8: return a.int8 == b.int8
    of Int16: return a.int16 == b.int16
    of Int32: return a.int32 == b.int32
    of Int64: return a.int64 == b.int64
    of UInt8: return a.uint8 == b.uint8
    of UInt16: return a.uint16 == b.uint16
    of UInt32: return a.uint32 == b.uint32
    of UInt64: return a.uint64 == b.uint64
    of Vec2I32: return a.vec2i32 == b.vec2i32
    of Vec2I64: return a.vec2i64 == b.vec2i64
    of Vec3I32: return a.vec3i32 == b.vec3i32
    of Vec3I64: return a.vec3i64 == b.vec3i64
    of Vec4I32: return a.vec4i32 == b.vec4i32
    of Vec4I64: return a.vec4i64 == b.vec4i64
    of Vec2U32: return a.vec2u32 == b.vec2u32
    of Vec2U64: return a.vec2u64 == b.vec2u64
    of Vec3U32: return a.vec3u32 == b.vec3u32
    of Vec3U64: return a.vec3u64 == b.vec3u64
    of Vec4U32: return a.vec4u32 == b.vec4u32
    of Vec4U64: return a.vec4u64 == b.vec4u64
    of Vec2F32: return a.vec2f32 == b.vec2f32
    of Vec2F64: return a.vec2f64 == b.vec2f64
    of Vec3F32: return a.vec3f32 == b.vec3f32
    of Vec3F64: return a.vec3f64 == b.vec3f64
    of Vec4F32: return a.vec4f32 == b.vec4f32
    of Vec4F64: return a.vec4f64 == b.vec4f64
    of Mat2F32: return a.mat2f32 == b.mat2f32
    of Mat2F64: return a.mat2f64 == b.mat2f64
    of Mat23F32: return a.mat23f32 == b.mat23f32
    of Mat23F64: return a.mat23f64 == b.mat23f64
    of Mat32F32: return a.mat32f32 == b.mat32f32
    of Mat32F64: return a.mat32f64 == b.mat32f64
    of Mat3F32: return a.mat3f32 == b.mat3f32
    of Mat3F64: return a.mat3f64 == b.mat3f64
    of Mat34F32: return a.mat34f32 == b.mat34f32
    of Mat34F64: return a.mat34f64 == b.mat34f64
    of Mat43F32: return a.mat43f32 == b.mat43f32
    of Mat43F64: return a.mat43f64 == b.mat43f64
    of Mat4F32: return a.mat4f32 == b.mat4f32
    of Mat4F64: return a.mat4f64 == b.mat4f64
    of Sampler2D: raise newException(Exception, "'==' not defined for Sampler2D")

func vertexInputs*(attributes: seq[ShaderAttribute]): seq[ShaderAttribute] =
  for attr in attributes:
    if attr.perInstance == false:
      result.add attr

func instanceInputs*(attributes: seq[ShaderAttribute]): seq[ShaderAttribute] =
  for attr in attributes:
    if attr.perInstance == false:
      result.add attr

func numberOfVertexInputAttributeDescriptors*(theType: DataType): int =
  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): int =
  case theType:
    of Float32: 4
    of Float64: 8
    of Int8: 1
    of Int16: 2
    of Int32: 4
    of Int64: 8
    of UInt8: 1
    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
    of Sampler2D: 0

func size*(attribute: ShaderAttribute, perDescriptor=false): int =
  if perDescriptor:
    attribute.theType.size div attribute.theType.numberOfVertexInputAttributeDescriptors
  else:
    if attribute.arrayCount == 0:
      attribute.theType.size
    else:
      attribute.theType.size * attribute.arrayCount

func size*(theType: seq[ShaderAttribute]): int =
  for attribute in theType:
    result += attribute.size

func size*(value: DataValue): int =
  value.theType.size

func size*(value: DataList): int =
  value.theType.size * value.len

func getDataType*[T: GPUType|int|uint|float](): DataType =
  when T is float32: Float32
  elif T is float64: Float64
  elif T is int8: Int8
  elif T is int16: Int16
  elif T is int32: Int32
  elif T is int64: Int64
  elif T is uint8: UInt8
  elif T is uint16: UInt16
  elif T is uint32: UInt32
  elif T is uint64: UInt64
  elif T is int and sizeof(int) == sizeof(int64): Int64
  elif T is int and sizeof(int) == sizeof(int32): Int32
  elif T is uint and sizeof(uint) == sizeof(uint64): UInt64
  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
  elif T is Sampler2DType: Sampler2D
  else:
    static:
      raise newException(Exception, &"Unsupported data type for GPU data: {name(T)}" )

func attr*[T: GPUType](
  name: string,
  perInstance=false,
  arrayCount=0,
  noInterpolation=false,
  memoryPerformanceHint=PreferFastRead,
): auto =
  ShaderAttribute(
    name: name,
    theType: getDataType[T](),
    perInstance: perInstance,
    arrayCount: arrayCount,
    noInterpolation: noInterpolation,
    memoryPerformanceHint: memoryPerformanceHint,
  )

func getValue*[T: GPUType|int|uint|float](value: DataValue): T =
  when T is float32: value.float32
  elif T is float64: value.float64
  elif T is int8: value.int8
  elif T is int16: value.int16
  elif T is int32: value.int32
  elif T is int64: value.int64
  elif T is uint8: value.uint8
  elif T is uint16: value.uint16
  elif T is uint32: value.uint32
  elif T is uint64: value.uint64
  elif T is int and sizeof(int) == sizeof(int32): value.int32
  elif T is int and sizeof(int) == sizeof(int64): value.int64
  elif T is uint and sizeof(uint) == sizeof(uint32): value.uint32
  elif T is uint and sizeof(uint) == sizeof(uint64): value.uint64
  elif T is float and sizeof(float) == sizeof(float32): value.float32
  elif T is float and sizeof(float) == sizeof(float64): value.float64
  elif T is TVec2[int32]: value.vec2i32
  elif T is TVec2[int64]: value.vec2i64
  elif T is TVec3[int32]: value.vec3i32
  elif T is TVec3[int64]: value.vec3i64
  elif T is TVec4[int32]: value.vec4i32
  elif T is TVec4[int64]: value.vec4i64
  elif T is TVec2[uint32]: value.vec2u32
  elif T is TVec2[uint64]: value.vec2u64
  elif T is TVec3[uint32]: value.vec3u32
  elif T is TVec3[uint64]: value.vec3u64
  elif T is TVec4[uint32]: value.vec4u32
  elif T is TVec4[uint64]: value.vec4u64
  elif T is TVec2[float32]: value.vec2f32
  elif T is TVec2[float64]: value.vec2f64
  elif T is TVec3[float32]: value.vec3f32
  elif T is TVec3[float64]: value.vec3f64
  elif T is TVec4[float32]: value.vec4f32
  elif T is TVec4[float64]: value.vec4f64
  elif T is TMat2[float32]: value.mat2f32
  elif T is TMat2[float64]: value.mat2f64
  elif T is TMat23[float32]: value.mat23f
  elif T is TMat23[float64]: value.mat23f64
  elif T is TMat32[float32]: value.mat32f32
  elif T is TMat32[float64]: value.mat32f64
  elif T is TMat3[float32]: value.mat3f32
  elif T is TMat3[float64]: value.mat3f64
  elif T is TMat34[float32]: value.mat34f32
  elif T is TMat34[float64]: value.mat34f64
  elif T is TMat43[float32]: value.mat43f32
  elif T is TMat43[float64]: value.mat43f64
  elif T is TMat4[float32]: value.mat4f32
  elif T is TMat4[float64]: value.mat4f64
  else: {.error: "Virtual datatype has no value" .}

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
  else: {. error: "Virtual datatype has no values" .}

func newDataList*(theType: DataType): DataList =
  result = DataList(theType: theType)
  case result.theType
    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 =
  result = newDataList(getDataType[T]())
  if len > 0:
    result.initData(len)

func newDataList*[T: GPUType](data: seq[T]): DataList =
  result = newDataList(getDataType[T]())
  setValues[T](result, data)

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
  elif T is int16: value.int16
  elif T is int32: value.int32
  elif T is int64: value.int64
  elif T is uint8: value.uint8
  elif T is uint16: value.uint16
  elif T is uint32: value.uint32
  elif T is uint64: value.uint64
  elif T is int and sizeof(int) == sizeof(int32): value.int32
  elif T is int and sizeof(int) == sizeof(int64): value.int64
  elif T is uint and sizeof(uint) == sizeof(uint32): value.uint32
  elif T is uint and sizeof(uint) == sizeof(uint64): value.uint64
  elif T is float and sizeof(float) == sizeof(float32): value.float32
  elif T is float and sizeof(float) == sizeof(float64): value.float64
  elif T is TVec2[int32]: value.vec2i32
  elif T is TVec2[int64]: value.vec2i64
  elif T is TVec3[int32]: value.vec3i32
  elif T is TVec3[int64]: value.vec3i64
  elif T is TVec4[int32]: value.vec4i32
  elif T is TVec4[int64]: value.vec4i64
  elif T is TVec2[uint32]: value.vec2u32
  elif T is TVec2[uint64]: value.vec2u64
  elif T is TVec3[uint32]: value.vec3u32
  elif T is TVec3[uint64]: value.vec3u64
  elif T is TVec4[uint32]: value.vec4u32
  elif T is TVec4[uint64]: value.vec4u64
  elif T is TVec2[float32]: value.vec2f32
  elif T is TVec2[float64]: value.vec2f64
  elif T is TVec3[float32]: value.vec3f32
  elif T is TVec3[float64]: value.vec3f64
  elif T is TVec4[float32]: value.vec4f32
  elif T is TVec4[float64]: value.vec4f64
  elif T is TMat2[float32]: value.mat2f32
  elif T is TMat2[float64]: value.mat2f64
  elif T is TMat23[float32]: value.mat23f
  elif T is TMat23[float64]: value.mat23f64
  elif T is TMat32[float32]: value.mat32f32
  elif T is TMat32[float64]: value.mat32f64
  elif T is TMat3[float32]: value.mat3f32
  elif T is TMat3[float64]: value.mat3f64
  elif T is TMat34[float32]: value.mat34f32
  elif T is TMat34[float64]: value.mat34f64
  elif T is TMat43[float32]: value.mat43f32
  elif T is TMat43[float64]: value.mat43f64
  elif T is TMat4[float32]: value.mat4f32
  elif T is TMat4[float64]: value.mat4f64
  else: {. error: "Virtual datatype has no values" .}

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
    of Float64: result[0] = addr value.float64
    of Int8: result[0] = addr value.int8
    of Int16: result[0] = addr value.int16
    of Int32: result[0] = addr value.int32
    of Int64: result[0] = addr value.int64
    of UInt8: result[0] = addr value.uint8
    of UInt16: result[0] = addr value.uint16
    of UInt32: result[0] = addr value.uint32
    of UInt64: result[0] = addr value.uint64
    of Vec2I32: result[0] = addr value.vec2i32
    of Vec2I64: result[0] = addr value.vec2i64
    of Vec3I32: result[0] = addr value.vec3i32
    of Vec3I64: result[0] = addr value.vec3i64
    of Vec4I32: result[0] = addr value.vec4i32
    of Vec4I64: result[0] = addr value.vec4i64
    of Vec2U32: result[0] = addr value.vec2u32
    of Vec2U64: result[0] = addr value.vec2u64
    of Vec3U32: result[0] = addr value.vec3u32
    of Vec3U64: result[0] = addr value.vec3u64
    of Vec4U32: result[0] = addr value.vec4u32
    of Vec4U64: result[0] = addr value.vec4u64
    of Vec2F32: result[0] = addr value.vec2f32
    of Vec2F64: result[0] = addr value.vec2f64
    of Vec3F32: result[0] = addr value.vec3f32
    of Vec3F64: result[0] = addr value.vec3f64
    of Vec4F32: result[0] = addr value.vec4f32
    of Vec4F64: result[0] = addr value.vec4f64
    of Mat2F32: result[0] = addr value.mat2f32
    of Mat2F64: result[0] = addr value.mat2f64
    of Mat23F32: result[0] = addr value.mat23f32
    of Mat23F64: result[0] = addr value.mat23f64
    of Mat32F32: result[0] = addr value.mat32f32
    of Mat32F64: result[0] = addr value.mat32f64
    of Mat3F32: result[0] = addr value.mat3f32
    of Mat3F64: result[0] = addr value.mat3f64
    of Mat34F32: result[0] = addr value.mat34f32
    of Mat34F64: result[0] = addr value.mat34f64
    of Mat43F32: result[0] = addr value.mat43f32
    of Mat43F64: result[0] = addr value.mat43f64
    of Mat4F32: result[0] = addr value.mat4f32
    of Mat4F64: result[0] = addr value.mat4f64
    of Sampler2D: result[0] = nil

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] = 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 Sampler2D: discard

func setValue*[T: GPUType|int|uint|float](value: var DataValue, data: 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 value" .}

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
  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
  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
  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
  else: {. error: "Virtual datatype has no values" .}

const TYPEMAP = {
    Float32: VK_FORMAT_R32_SFLOAT,
    Float64: VK_FORMAT_R64_SFLOAT,
    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*(theType: DataType): VkFormat =
  TYPEMAP[theType]

# from https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap15.html
func nLocationSlots*(theType: DataType): int =
  #[
  single location:
    16-bit scalar and vector types, and
    32-bit scalar and vector types, and
    64-bit scalar and 2-component vector types.
  two locations
    64-bit three- and four-component vectors
  ]#
  case theType:
    of Float32: 1
    of Float64: 1
    of Int8: 1
    of Int16: 1
    of Int32: 1
    of Int64: 1
    of UInt8: 1
    of UInt16: 1
    of UInt32: 1
    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
    of Sampler2D: 1

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..
  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"
    of Sampler2D: "sampler2D"

func glslInput*(group: openArray[ShaderAttribute]): seq[string] =
  if group.len == 0:
    return @[]
  var i = 0
  for attribute in group:
    assert attribute.arrayCount == 0, "arrays not supported for shader vertex attributes"
    let flat = if attribute.noInterpolation: "flat " else: ""
    result.add &"layout(location = {i}) {flat}in {attribute.theType.glslType} {attribute.name};"
    for j in 0 ..< attribute.theType.numberOfVertexInputAttributeDescriptors:
      i += attribute.theType.nLocationSlots

func glslUniforms*(group: openArray[ShaderAttribute], blockName="Uniforms", binding: int): seq[string] =
  if group.len == 0:
    return @[]
  # currently only a single uniform block supported, therefore binding = 0
  result.add(&"layout(std430, binding = {binding}) uniform T{blockName} {{")
  for attribute in group:
    var arrayDecl = ""
    if attribute.arrayCount > 0:
      arrayDecl = &"[{attribute.arrayCount}]"
    result.add(&"    {attribute.theType.glslType} {attribute.name}{arrayDecl};")
  result.add(&"}} {blockName};")

func glslSamplers*(group: openArray[ShaderAttribute], basebinding: int): seq[string] =
  if group.len == 0:
    return @[]
  var thebinding = basebinding
  for attribute in group:
    var arrayDecl = ""
    if attribute.arrayCount > 0:
      arrayDecl = &"[{attribute.arrayCount}]"
    result.add(&"layout(binding = {thebinding}) uniform {attribute.theType.glslType} {attribute.name}{arrayDecl};")
    inc thebinding

func glslOutput*(group: openArray[ShaderAttribute]): seq[string] =
  if group.len == 0:
    return @[]
  var i = 0
  for attribute in group:
    assert attribute.arrayCount == 0, "arrays not supported for outputs"
    let flat = if attribute.noInterpolation: "flat " else: ""
    result.add &"layout(location = {i}) {flat}out {attribute.theType.glslType} {attribute.name};"
    i += 1