changeset 583:90537a8887ae

did: implement uniforms, some refactoring
author Sam <sam@basx.dev>
date Sun, 09 Apr 2023 01:04:54 +0700
parents 8cf5d3f921ae
children 9e7bcc8e0e52
files src/semicongine/entity.nim src/semicongine/gpu_data.nim src/semicongine/mesh.nim src/semicongine/platform/linux/xlib.nim src/semicongine/platform/windows/win32.nim src/semicongine/scene.nim src/semicongine/vulkan/instance.nim src/semicongine/vulkan/pipeline.nim src/semicongine/vulkan/shader.nim src/semicongine/vulkan/swapchain.nim tests/test_vulkan_wrapper.nim
diffstat 11 files changed, 318 insertions(+), 145 deletions(-) [+]
line wrap: on
line diff
--- a/src/semicongine/entity.nim	Fri Apr 07 00:32:07 2023 +0700
+++ b/src/semicongine/entity.nim	Sun Apr 09 01:04:54 2023 +0700
@@ -16,23 +16,23 @@
 
 
 method `$`*(entity: Entity): string {.base.} = entity.name
-method `$`*(part: Component): string {.base.} =
+method `$`*(component: Component): string {.base.} =
   "Unknown Component"
 
 proc add*(entity: Entity, child: Entity) =
   child.parent = entity
   entity.children.add child
-proc add*(entity: Entity, part: Component) =
-  part.entity = entity
-  entity.components.add part
+proc add*(entity: Entity, component: Component) =
+  component.entity = entity
+  entity.components.add component
 proc add*(entity: Entity, children: seq[Entity]) =
   for child in children:
     child.parent = entity
     entity.children.add child
 proc add*(entity: Entity, components: seq[Component]) =
-  for part in components:
-    part.entity = entity
-    entity.components.add part
+  for component in components:
+    component.entity = entity
+    entity.components.add component
 
 func newEntity*(name: string = ""): Entity =
   result = new Entity
@@ -51,12 +51,12 @@
   if result.name == "":
     result.name = &"Entity[{$(cast[ByteAddress](result))}]"
 
-proc newEntity*(name: string, firstPart: Component, components: varargs[Component]): Entity =
+proc newEntity*(name: string, firstComponent: Component, components: varargs[Component]): Entity =
   result = new Entity
   result.name = name
-  result.add firstPart
-  for part in components:
-    result.add part
+  result.add firstComponent
+  for component in components:
+    result.add component
   if result.name == "":
     result.name = &"Entity[{$(cast[ByteAddress](result))}]"
   result.transform = Unit4
@@ -68,13 +68,13 @@
     result = currentEntity.transform * result
     currentEntity = currentEntity.parent
 
-iterator allPartsOfType*[T: Component](root: Entity): T =
+iterator allComponentsOfType*[T: Component](root: Entity): T =
   var queue = @[root]
   while queue.len > 0:
     let entity = queue.pop
-    for part in entity.components:
-      if part of T:
-        yield T(part)
+    for component in entity.components:
+      if component of T:
+        yield T(component)
     for i in countdown(entity.children.len - 1, 0):
       queue.add entity.children[i]
 
@@ -87,15 +87,15 @@
         return child
       queue.add child
 
-func firstPartWithName*[T: Component](root: Entity, name: string): T =
+func firstComponentWithName*[T: Component](root: Entity, name: string): T =
   var queue = @[root]
   while queue.len > 0:
     let next = queue.pop
     for child in next.children:
       if child.name == name:
-        for part in child.components:
-          if part of T:
-            return T(part)
+        for component in child.components:
+          if component of T:
+            return T(component)
       queue.add child
 
 func allWithName*(root: Entity, name: string): seq[Entity] =
@@ -107,15 +107,15 @@
         result.add child
       queue.add child
 
-func allPartsWithName*[T: Component](root: Entity, name: string): seq[T] =
+func allComponentsWithName*[T: Component](root: Entity, name: string): seq[T] =
   var queue = @[root]
   while queue.len > 0:
     let next = queue.pop
     for child in next.children:
       if child.name == name:
-        for part in child.components:
-          if part of T:
-            result.add T(part)
+        for component in child.components:
+          if component of T:
+            result.add T(component)
       queue.add child
 
 iterator allEntities*(root: Entity): Entity =
--- a/src/semicongine/gpu_data.nim	Fri Apr 07 00:32:07 2023 +0700
+++ b/src/semicongine/gpu_data.nim	Sun Apr 09 01:04:54 2023 +0700
@@ -1,4 +1,3 @@
-import std/sequtils
 import std/typetraits
 import std/strformat
 import std/tables
@@ -99,24 +98,19 @@
     of Mat4F64: mat4f64: TMat4[float64]
   MemoryLocation* = enum
     VRAM, VRAMVisible, RAM # VRAM is fastest, VRAMVisible allows updating memory directly, may be slower
-  VertexAttribute* = object
+  ShaderAttribute* = object
     name*: string
     thetype*: DataType
     perInstance*: bool
     memoryLocation*: MemoryLocation
-  AttributeGroup* = object
-    attributes*: seq[VertexAttribute]
 
-func initAttributeGroup*(attrs: varargs[VertexAttribute]): auto =
-  AttributeGroup(attributes: attrs.toSeq)
-
-func vertexInputs*(group: AttributeGroup): seq[VertexAttribute] =
-  for attr in group.attributes:
+func vertexInputs*(attributes: seq[ShaderAttribute]): seq[ShaderAttribute] =
+  for attr in attributes:
     if attr.perInstance == false:
       result.add attr
 
-func instanceInputs*(group: AttributeGroup): seq[VertexAttribute] =
-  for attr in group.attributes:
+func instanceInputs*(attributes: seq[ShaderAttribute]): seq[ShaderAttribute] =
+  for attr in attributes:
     if attr.perInstance == false:
       result.add attr
 
@@ -173,12 +167,12 @@
     of Mat4F32: 64
     of Mat4F64: 128
   
-func size*(attribute: VertexAttribute, perDescriptor=false): uint32 =
+func size*(attribute: ShaderAttribute, perDescriptor=false): uint32 =
   if perDescriptor: attribute.thetype.size div attribute.thetype.numberOfVertexInputAttributeDescriptors
   else:      attribute.thetype.size
 
-func size*(thetype: AttributeGroup): uint32 =
-  for attribute in thetype.attributes:
+func size*(thetype: seq[ShaderAttribute]): uint32 =
+  for attribute in thetype:
     result += attribute.size
 
 func getDataType*[T: GPUType|int|uint|float](): DataType =
@@ -239,7 +233,7 @@
   perInstance=false,
   memoryLocation=VRAMVisible,
 ): auto =
-  VertexAttribute(
+  ShaderAttribute(
     name: name,
     thetype: getDataType[T](),
     perInstance: perInstance,
@@ -247,49 +241,150 @@
   )
 
 func get*[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
+
+proc getRawData*(value: var DataValue): (pointer, uint64) =
+  result[1] = value.thetype.size
   case value.thetype
-    of Float32: value.float32
-    of Float64: value.float64
-    of Int8: value.int8
-    of Int16: value.int16
-    of Int32: value.int32
-    of Int64: value.int64
-    of UInt8: value.uint8
-    of UInt16: value.uint16
-    of UInt32: value.uint32
-    of UInt64: value.uint64
-    of Vec2I32: value.vec2i32
-    of Vec2I64: value.vec2i64
-    of Vec3I32: value.vec3i32
-    of Vec3I64: value.vec3i64
-    of Vec4I32: value.vec4i32
-    of Vec4I64: value.vec4i64
-    of Vec2U32: value.vec2u32
-    of Vec2U64: value.vec2u64
-    of Vec3U32: value.vec3u32
-    of Vec3U64: value.vec3u64
-    of Vec4U32: value.vec4u32
-    of Vec4U64: value.vec4u64
-    of Vec2F32: value.vec2f32
-    of Vec2F64: value.vec2f64
-    of Vec3F32: value.vec3f32
-    of Vec3F64: value.vec3f64
-    of Vec4F32: value.vec4f32
-    of Vec4F64: value.vec4f64
-    of Mat2F32: value.mat2f32
-    of Mat2F64: value.mat2f64
-    of Mat23F32: value.mat23f32
-    of Mat23F64: value.mat23f64
-    of Mat32F32: value.mat32f32
-    of Mat32F64: value.mat32f64
-    of Mat3F32: value.mat3f32
-    of Mat3F64: value.mat3f64
-    of Mat34F32: value.mat34f32
-    of Mat34F64: value.mat34f64
-    of Mat43F32: value.mat43f32
-    of Mat43F64: value.mat43f64
-    of Mat4F32: value.mat4f32
-    of Mat4F64: value.mat4f64
+    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
+
+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
 
 const TYPEMAP = {
     Float32: VK_FORMAT_R32_SFLOAT,
@@ -436,33 +531,33 @@
     of Mat4F32: "mat4"
     of Mat4F64: "dmat4"
 
-func glslInput*(group: AttributeGroup): seq[string] =
-  if group.attributes.len == 0:
+func glslInput*(group: seq[ShaderAttribute]): seq[string] =
+  if group.len == 0:
     return @[]
   var i = 0'u32
-  for attribute in group.attributes:
+  for attribute in group:
     result.add &"layout(location = {i}) in {attribute.thetype.glslType} {attribute.name};"
     for j in 0 ..< attribute.thetype.numberOfVertexInputAttributeDescriptors:
       i += attribute.thetype.nLocationSlots
 
-func glslUniforms*(group: AttributeGroup, blockName="Uniforms", binding=0): seq[string] =
-  if group.attributes.len == 0:
+func glslUniforms*(group: seq[ShaderAttribute], blockName="Uniforms", binding=0): seq[string] =
+  if group.len == 0:
     return @[]
   # currently only a single uniform block supported, therefore binding = 0
   result.add(&"layout(binding = {binding}) uniform T{blockName} {{")
-  for attribute in group.attributes:
+  for attribute in group:
     result.add(&"    {attribute.thetype.glslType} {attribute.name};")
   result.add(&"}} {blockName};")
 
-func glslOutput*(group: AttributeGroup): seq[string] =
-  if group.attributes.len == 0:
+func glslOutput*(group: seq[ShaderAttribute]): seq[string] =
+  if group.len == 0:
     return @[]
   var i = 0'u32
-  for attribute in group.attributes:
+  for attribute in group:
     result.add &"layout(location = {i}) out {attribute.thetype.glslType} {attribute.name};"
     i += 1
 
-func groupByMemoryLocation*(attributes: openArray[VertexAttribute]): Table[MemoryLocation, seq[VertexAttribute]] =
+func groupByMemoryLocation*(attributes: openArray[ShaderAttribute]): Table[MemoryLocation, seq[ShaderAttribute]] =
   for attr in attributes:
     if not (attr.memoryLocation in result):
       result[attr.memoryLocation] = @[]
--- a/src/semicongine/mesh.nim	Fri Apr 07 00:32:07 2023 +0700
+++ b/src/semicongine/mesh.nim	Sun Apr 09 01:04:54 2023 +0700
@@ -28,7 +28,7 @@
       of TextureCoordinate: texturecoord: seq[Vec2f]
   Mesh* = ref object of Component
     vertexCount*: uint32
-    data: Table[VertexAttribute, MeshData]
+    data: Table[ShaderAttribute, MeshData]
     case indexType*: MeshIndexType
       of None: discard
       of Tiny: tinyIndices*: seq[array[3, uint8]]
@@ -134,7 +134,7 @@
     of BiTangent: meshdata.bitangent.size
     of TextureCoordinate: meshdata.texturecoord.size
 
-func attributeSize*(mesh: Mesh, attribute: VertexAttribute): uint64 =
+func attributeSize*(mesh: Mesh, attribute: ShaderAttribute): uint64 =
   mesh.data[attribute].meshDataSize
 
 func vertexDataSize*(mesh: Mesh): uint64 =
@@ -167,14 +167,14 @@
     of Small: rawData(mesh.smallIndices)
     of Big: rawData(mesh.bigIndices)
 
-proc hasDataFor*(mesh: Mesh, attribute: VertexAttribute): bool =
+proc hasDataFor*(mesh: Mesh, attribute: ShaderAttribute): bool =
   assert attribute.perInstance == false, "Mesh data cannot handle per-instance attributes"
   attribute in mesh.data
 
-proc getRawData*(mesh: Mesh, attribute: VertexAttribute): (pointer, uint64) =
+proc getRawData*(mesh: Mesh, attribute: ShaderAttribute): (pointer, uint64) =
   assert attribute.perInstance == false, "Mesh data cannot handle per-instance attributes"
   mesh.data[attribute].getRawData()
 
-proc getData*(mesh: Mesh, attribute: VertexAttribute): MeshData =
+proc getData*(mesh: Mesh, attribute: ShaderAttribute): MeshData =
   assert attribute.perInstance == false, "Mesh data cannot handle per-instance attributes"
   mesh.data[attribute]
--- a/src/semicongine/platform/linux/xlib.nim	Fri Apr 07 00:32:07 2023 +0700
+++ b/src/semicongine/platform/linux/xlib.nim	Sun Apr 09 01:04:54 2023 +0700
@@ -62,7 +62,7 @@
   return NativeWindow(display: display, window: window,
       emptyCursor: empty_cursor)
 
-proc trash*(window: NativeWindow) =
+proc destroy*(window: NativeWindow) =
   checkXlibResult window.display.XFreeCursor(window.emptyCursor)
   checkXlibResult window.display.XDestroyWindow(window.window)
   discard window.display.XCloseDisplay() # always returns 0
--- a/src/semicongine/platform/windows/win32.nim	Fri Apr 07 00:32:07 2023 +0700
+++ b/src/semicongine/platform/windows/win32.nim	Sun Apr 09 01:04:54 2023 +0700
@@ -88,7 +88,7 @@
   discard ShowWindow(result.hwnd, SW_SHOW)
   discard ShowCursor(false)
 
-proc trash*(window: NativeWindow) =
+proc destroy*(window: NativeWindow) =
   discard
 
 proc size*(window: NativeWindow): (int, int) =
@@ -106,9 +106,9 @@
     DispatchMessage(addr(msg))
   return currentEvents
 
-proc getMousePosition*(window: NativeWindow): Option[Vec2] =
+proc getMousePosition*(window: NativeWindow): Option[Vec2f] =
   var p: POINT
   let res = GetCursorPos(addr(p))
   if res:
-    return some(Vec2([float32(p.x), float32(p.y)]))
-  return none(Vec2)
+    return some(Vec2f([float32(p.x), float32(p.y)]))
+  return none(Vec2f)
--- a/src/semicongine/scene.nim	Fri Apr 07 00:32:07 2023 +0700
+++ b/src/semicongine/scene.nim	Sun Apr 09 01:04:54 2023 +0700
@@ -38,14 +38,22 @@
   else:
     &"Drawable(elementCount: {drawable.elementCount}, instanceCount: {drawable.instanceCount}, buffer: {drawable.buffer}, offsets: {drawable.offsets})"
 
+func `$`*(global: ShaderGlobal): string =
+  &"ShaderGlobal(name: {global.name}, {global.value})"
+
+func initShaderGlobal*[T](name: string, data: T): ShaderGlobal =
+  var value = DataValue(thetype: getDataType[T]())
+  value.setValue(data)
+  ShaderGlobal(name: name, value: value)
+
 proc getBuffers*(scene: Scene, pipeline: VkPipeline): seq[Buffer] =
   var counted: seq[VkBuffer]
   for drawable in scene.drawables[pipeline]:
     if not (drawable.buffer.vk in counted):
-      result.add(drawable.buffer)
+      result.add drawable.buffer
       counted.add drawable.buffer.vk
     if drawable.indexed and not (drawable.indexBuffer.vk in counted):
-      result.add(drawable.indexBuffer)
+      result.add drawable.indexBuffer
       counted.add drawable.indexBuffer.vk
 
 proc destroy*(scene: var Scene, pipeline: VkPipeline) =
@@ -70,7 +78,7 @@
     smallIndexedMeshes: seq[Mesh]
     bigIndexedMeshes: seq[Mesh]
     allIndexedMeshes: seq[Mesh]
-  for mesh in allPartsOfType[Mesh](scene.root):
+  for mesh in allComponentsOfType[Mesh](scene.root):
     for inputAttr in pipeline.inputs.vertexInputs:
       assert mesh.hasDataFor(inputAttr), &"{mesh} missing data for {inputAttr}"
     case mesh.indexType:
@@ -148,11 +156,10 @@
         buffer.setData(pdata, size, bufferOffset)
         bufferOffset += size
       scene.drawables[pipeline.vk].add drawable
-  echo scene.getBuffers(pipeline.vk)
 
-proc setupDrawables*(scene: var Scene, renderPass: var RenderPass) =
-  for subpass in renderPass.subpasses.mitems:
-    for pipeline in subpass.pipelines.mitems:
+proc setupDrawables*(scene: var Scene, renderPass: RenderPass) =
+  for subpass in renderPass.subpasses:
+    for pipeline in subpass.pipelines:
       scene.setupDrawables(pipeline)
 
 proc getDrawables*(scene: Scene, pipeline: Pipeline): seq[Drawable] =
--- a/src/semicongine/vulkan/instance.nim	Fri Apr 07 00:32:07 2023 +0700
+++ b/src/semicongine/vulkan/instance.nim	Sun Apr 09 01:04:54 2023 +0700
@@ -108,6 +108,7 @@
   log LEVEL_MAPPING[messageSeverity], &"{toEnums messageTypes}: {pCallbackData.pMessage}"
   if messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
     echo getStackTrace()
+    quit()
   return false
 
 proc createDebugMessenger*(
--- a/src/semicongine/vulkan/pipeline.nim	Fri Apr 07 00:32:07 2023 +0700
+++ b/src/semicongine/vulkan/pipeline.nim	Sun Apr 09 01:04:54 2023 +0700
@@ -1,3 +1,6 @@
+import std/tables
+import std/sequtils
+
 import ./api
 import ./device
 import ./descriptor
@@ -16,35 +19,41 @@
     descriptorPool*: DescriptorPool
     descriptorSets*: seq[DescriptorSet]
 
-func inputs*(pipeline: Pipeline): AttributeGroup =
+func inputs*(pipeline: Pipeline): seq[ShaderAttribute] =
   for shader in pipeline.shaders:
     if shader.stage == VK_SHADER_STAGE_VERTEX_BIT:
       return shader.inputs
 
+func uniforms*(pipeline: Pipeline): seq[ShaderAttribute] =
+  var uniformList: Table[string, ShaderAttribute]
+  for shader in pipeline.shaders:
+    for attribute in shader.uniforms:
+      if attribute.name in uniformList:
+        assert uniformList[attribute.name] == attribute
+      else:
+        uniformList[attribute.name] = attribute
+  result = uniformList.values.toSeq
+
 proc createPipeline*(device: Device, renderPass: VkRenderPass, vertexShader: Shader, fragmentShader: Shader, inFlightFrames: int, subpass = 0'u32): Pipeline =
   assert renderPass.valid
   assert device.vk.valid
   assert vertexShader.stage == VK_SHADER_STAGE_VERTEX_BIT
   assert fragmentShader.stage == VK_SHADER_STAGE_FRAGMENT_BIT
+  assert vertexShader.outputs == fragmentShader.inputs
+  assert vertexShader.uniforms == fragmentShader.uniforms
 
   result.device = device
   result.shaders = @[vertexShader, fragmentShader]
   
-  var descriptors = @[Descriptor(
-    thetype: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
-    count: 1,
-    stages: @[VK_SHADER_STAGE_VERTEX_BIT],
-    itemsize: vertexShader.uniforms.size(),
-  )]
-  if vertexShader.uniforms == fragmentShader.uniforms:
-    descriptors[0].stages.add VK_SHADER_STAGE_FRAGMENT_BIT
-  else:
-    descriptors.add Descriptor(
+  # TODO: correct descriptors over all shaders
+  var descriptors = @[
+    Descriptor(
       thetype: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
       count: 1,
-      stages: @[VK_SHADER_STAGE_FRAGMENT_BIT],
-      itemsize: fragmentShader.uniforms.size(),
-    )
+      stages: @[VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_STAGE_FRAGMENT_BIT],
+      itemsize: vertexShader.uniforms.size(),
+    ),
+  ]
   result.descriptorSetLayout = device.createDescriptorSetLayout(descriptors)
 
   # TODO: Push constants
@@ -151,6 +160,7 @@
   )
   result.descriptorPool = result.device.createDescriptorSetPool(@[(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1'u32)])
   result.descriptorSets = result.descriptorPool.allocateDescriptorSet(result.descriptorSetLayout, inFlightFrames)
+  discard result.uniforms # just for assertion
 
 proc destroy*(pipeline: var Pipeline) =
   assert pipeline.device.vk.valid
--- a/src/semicongine/vulkan/shader.nim	Fri Apr 07 00:32:07 2023 +0700
+++ b/src/semicongine/vulkan/shader.nim	Sun Apr 09 01:04:54 2023 +0700
@@ -24,17 +24,17 @@
     stage: VkShaderStageFlagBits
     entrypoint: string
     binary: seq[uint32]
-    inputs: AttributeGroup
-    uniforms: AttributeGroup
-    outputs: AttributeGroup
+    inputs: seq[ShaderAttribute]
+    uniforms: seq[ShaderAttribute]
+    outputs: seq[ShaderAttribute]
   Shader* = object
     device: Device
     vk*: VkShaderModule
     stage*: VkShaderStageFlagBits
     entrypoint*: string
-    inputs*: AttributeGroup
-    uniforms*: AttributeGroup
-    outputs*: AttributeGroup
+    inputs*: seq[ShaderAttribute]
+    uniforms*: seq[ShaderAttribute]
+    outputs*: seq[ShaderAttribute]
 
 
 proc compileGlslToSPIRV(stage: VkShaderStageFlagBits, shaderSource: string, entrypoint: string): seq[uint32] {.compileTime.} =
@@ -86,17 +86,17 @@
 
 proc compileGlslShader*(
   stage: VkShaderStageFlagBits,
-  inputs=AttributeGroup(),
-  uniforms=AttributeGroup(),
-  outputs=AttributeGroup(),
+  inputs: seq[ShaderAttribute]= @[],
+  uniforms: seq[ShaderAttribute]= @[],
+  outputs: seq[ShaderAttribute]= @[],
   version=DEFAULT_SHADER_VERSION ,
   entrypoint=DEFAULT_SHADER_ENTRYPOINT ,
   body: seq[string]
 ): ShaderCode {.compileTime.} =
   var code = @[&"#version {version}", ""] &
-    (if inputs.attributes.len > 0: inputs.glslInput() & @[""] else: @[]) &
-    (if uniforms.attributes.len > 0: uniforms.glslUniforms() & @[""] else: @[]) &
-    (if outputs.attributes.len > 0: outputs.glslOutput() & @[""] else: @[]) &
+    (if inputs.len > 0: inputs.glslInput() & @[""] else: @[]) &
+    (if uniforms.len > 0: uniforms.glslUniforms() & @[""] else: @[]) &
+    (if outputs.len > 0: outputs.glslOutput() & @[""] else: @[]) &
     @[&"void {entrypoint}(){{"] &
     body &
     @[&"}}"]
@@ -110,9 +110,9 @@
 
 proc compileGlslShader*(
   stage: VkShaderStageFlagBits,
-  inputs: AttributeGroup=AttributeGroup(),
-  uniforms: AttributeGroup=AttributeGroup(),
-  outputs: AttributeGroup=AttributeGroup(),
+  inputs: seq[ShaderAttribute]= @[],
+  uniforms: seq[ShaderAttribute]= @[],
+  outputs: seq[ShaderAttribute]= @[],
   version=DEFAULT_SHADER_VERSION ,
   entrypoint=DEFAULT_SHADER_ENTRYPOINT ,
   body: string
@@ -150,7 +150,7 @@
   var location = 0'u32
   var binding = baseBinding
 
-  for attribute in shader.inputs.attributes:
+  for attribute in shader.inputs.vertexInputs:
     bindings.add VkVertexInputBindingDescription(
       binding: binding,
       stride: attribute.size,
--- a/src/semicongine/vulkan/swapchain.nim	Fri Apr 07 00:32:07 2023 +0700
+++ b/src/semicongine/vulkan/swapchain.nim	Sun Apr 09 01:04:54 2023 +0700
@@ -1,3 +1,4 @@
+import std/tables
 import std/options
 import std/logging
 
@@ -6,13 +7,17 @@
 import ./device
 import ./physicaldevice
 import ./image
+import ./buffer
 import ./renderpass
+import ./descriptor
 import ./framebuffer
 import ./commandbuffer
 import ./pipeline
 import ./syncing
 
 import ../scene
+import ../entity
+import ../gpu_data
 import ../math
 
 type
@@ -31,6 +36,7 @@
     imageAvailableSemaphore*: seq[Semaphore]
     renderFinishedSemaphore*: seq[Semaphore]
     commandBufferPool: CommandBufferPool
+    uniformBuffers: Table[VkPipeline, seq[Buffer]]
 
 
 proc createSwapchain*(
@@ -101,6 +107,47 @@
 
   return (swapchain, createResult)
 
+proc setupUniforms(swapChain: var Swapchain, scene: var Scene, pipeline: var Pipeline) =
+  assert pipeline.vk.valid
+  assert not (pipeline.vk in swapChain.uniformBuffers)
+
+  swapChain.uniformBuffers[pipeline.vk] = @[]
+
+  var uniformBufferSize = 0'u64
+  for uniform in pipeline.uniforms:
+    uniformBufferSize += uniform.thetype.size
+
+  for i in 0 ..< swapChain.renderPass.inFlightFrames:
+    var buffer = pipeline.device.createBuffer(
+      size=uniformBufferSize,
+      usage=[VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT],
+      useVRAM=true,
+      mappable=true,
+    )
+    swapChain.uniformBuffers[pipeline.vk].add buffer
+    pipeline.descriptorSets[i].setDescriptorSet(buffer)
+
+proc setupUniforms*(swapChain: var Swapchain, scene: var Scene) =
+  for subpass in swapChain.renderPass.subpasses.mitems:
+    for pipeline in subpass.pipelines.mitems:
+      swapChain.setupUniforms(scene, pipeline)
+
+proc updateUniforms*(swapChain: var Swapchain, scene: Scene, pipeline: Pipeline) =
+  assert pipeline.vk.valid
+  assert swapChain.uniformBuffers[pipeline.vk][swapChain.currentInFlight].vk.valid
+
+  var globalsByName: Table[string, DataValue]
+  for component in allComponentsOfType[ShaderGlobal](scene.root):
+    globalsByName[component.name] = component.value
+
+  var offset = 0'u64
+  for uniform in pipeline.uniforms:
+    assert uniform.thetype == globalsByName[uniform.name].thetype
+    let (pdata, size) = globalsByName[uniform.name].getRawData()
+    swapChain.uniformBuffers[pipeline.vk][swapChain.currentInFlight].setData(pdata, size, offset)
+    offset += size
+
+
 proc beginRenderCommands*(commandBuffer: VkCommandBuffer, renderpass: RenderPass, framebuffer: Framebuffer) =
   assert commandBuffer.valid
   assert renderpass.vk.valid
@@ -220,6 +267,7 @@
       for pipeline in swapchain.renderpass.subpasses[i].pipelines.mitems:
         commandBuffer.vkCmdBindPipeline(swapchain.renderpass.subpasses[i].pipelineBindPoint, pipeline.vk)
         commandBuffer.vkCmdBindDescriptorSets(swapchain.renderpass.subpasses[i].pipelineBindPoint, pipeline.layout, 0, 1, addr(pipeline.descriptorSets[swapchain.currentInFlight].vk), 0, nil)
+        swapchain.updateUniforms(scene, pipeline)
         commandBuffer.draw(scene.getDrawables(pipeline), scene)
       if i < swapchain.renderpass.subpasses.len - 1:
         commandBuffer.vkCmdNextSubpass(VK_SUBPASS_CONTENTS_INLINE)
@@ -280,6 +328,9 @@
     swapchain.queueFinishedFence[i].destroy()
     swapchain.imageAvailableSemaphore[i].destroy()
     swapchain.renderFinishedSemaphore[i].destroy()
+  for buffers in swapchain.uniformBuffers.mvalues:
+    for buffer in buffers.mitems:
+      buffer.destroy()
+
   swapchain.device.vk.vkDestroySwapchainKHR(swapchain.vk, nil)
   swapchain.vk.reset()
-
--- a/tests/test_vulkan_wrapper.nim	Fri Apr 07 00:32:07 2023 +0700
+++ b/tests/test_vulkan_wrapper.nim	Sun Apr 09 01:04:54 2023 +0700
@@ -1,4 +1,3 @@
-import std/os
 import std/options
 
 import semicongine
@@ -55,21 +54,25 @@
 
   # INIT RENDERER:
   const
-    vertexInput = initAttributeGroup(
+    vertexInput = @[
       attr[Vec3f]("position"),
       attr[Vec3f]("color"),
-    )
-    vertexOutput = initAttributeGroup(attr[Vec3f]("outcolor"))
-    fragOutput = initAttributeGroup(attr[Vec4f]("color"))
+      attr[Mat4]("transform", perInstance=true)
+    ]
+    vertexOutput = @[attr[Vec3f]("outcolor")]
+    uniforms = @[attr[float32]("time")]
+    fragOutput = @[attr[Vec4f]("color")]
     vertexCode = compileGlslShader(
       stage=VK_SHADER_STAGE_VERTEX_BIT,
       inputs=vertexInput,
+      uniforms=uniforms,
       outputs=vertexOutput,
-      body="""gl_Position = vec4(position, 1.0); outcolor = color;"""
+      body="""gl_Position = vec4(position, 1.0); outcolor = color * sin(Uniforms.time) * 0.5 + 0.5;"""
     )
     fragmentCode = compileGlslShader(
       stage=VK_SHADER_STAGE_FRAGMENT_BIT,
       inputs=vertexOutput,
+      uniforms=uniforms,
       outputs=fragOutput,
       body="color = vec4(outcolor, 1);"
     )
@@ -83,9 +86,11 @@
     raise newException(Exception, "Unable to create swapchain")
 
   # INIT SCENE
+  var time = initShaderGlobal("time", 0.0'f32)
   var thescene = Scene(
     name: "main",
     root: newEntity("root",
+      newEntity("stuff", time),
       newEntity("triangle1", newMesh(
         positions=[newVec3f(0.0, -0.5), newVec3f(0.5, 0.5), newVec3f(-0.5, 0.5)],
         colors=[newVec3f(1.0, 0.0, 0.0), newVec3f(0.0, 1.0, 0.0), newVec3f(0.0, 0.0, 1.0)],
@@ -119,10 +124,13 @@
     )
   )
   thescene.setupDrawables(renderPass)
+  swapchain.setupUniforms(thescene)
 
   # MAINLOOP
   echo "Setup successfull, start rendering"
-  for i in 0 ..< 10000:
+  for i in 0 ..< 1:
+    setValue[float32](time.value, get[float32](time.value) + 0.0005)
+    echo get[float32](time.value)
     discard swapchain.drawScene(thescene)
   echo "Rendered ", swapchain.framesRendered, " frames"
   checkVkResult device.vk.vkDeviceWaitIdle()
@@ -143,3 +151,4 @@
   device.destroy()
   debugger.destroy()
   instance.destroy()
+  thewindow.destroy()