changeset 124:cb9e27a30165

fix: completely overhole buffer handling for drawing, fix shit
author Sam <sam@basx.dev>
date Mon, 10 Apr 2023 20:09:37 +0700
parents 55be3579dc30
children 6e2c48cb6f60
files src/semicongine/gpu_data.nim src/semicongine/mesh.nim src/semicongine/scene.nim src/semicongine/vulkan/buffer.nim src/semicongine/vulkan/swapchain.nim tests/test_vulkan_wrapper.nim
diffstat 6 files changed, 195 insertions(+), 198 deletions(-) [+]
line wrap: on
line diff
--- a/src/semicongine/gpu_data.nim	Mon Apr 10 00:53:16 2023 +0700
+++ b/src/semicongine/gpu_data.nim	Mon Apr 10 20:09:37 2023 +0700
@@ -777,14 +777,21 @@
     of Mat4F32: "mat4"
     of Mat4F64: "dmat4"
 
+func groupByMemoryLocation*(attributes: openArray[ShaderAttribute]): Table[MemoryLocation, seq[ShaderAttribute]] =
+  for attr in attributes:
+    if not (attr.memoryLocation in result):
+      result[attr.memoryLocation] = @[]
+    result[attr.memoryLocation].add attr
+
 func glslInput*(group: seq[ShaderAttribute]): seq[string] =
   if group.len == 0:
     return @[]
   var i = 0'u32
-  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
+  for attributes in group.groupByMemoryLocation().values:
+    for attribute in attributes:
+      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: seq[ShaderAttribute], blockName="Uniforms", binding=0): seq[string] =
   if group.len == 0:
@@ -802,9 +809,3 @@
   for attribute in group:
     result.add &"layout(location = {i}) out {attribute.thetype.glslType} {attribute.name};"
     i += 1
-
-func groupByMemoryLocation*(attributes: openArray[ShaderAttribute]): Table[MemoryLocation, seq[ShaderAttribute]] =
-  for attr in attributes:
-    if not (attr.memoryLocation in result):
-      result[attr.memoryLocation] = @[]
-    result[attr.memoryLocation].add attr
--- a/src/semicongine/mesh.nim	Mon Apr 10 00:53:16 2023 +0700
+++ b/src/semicongine/mesh.nim	Mon Apr 10 20:09:37 2023 +0700
@@ -19,8 +19,7 @@
     vertexCount*: uint32
     indicesCount*: uint32
     instanceCount*: uint32
-    vertexdata: Table[string, DataList]
-    instancedata: Table[string, DataList]
+    data: Table[string, DataList]
     case indexType*: MeshIndexType
       of None: discard
       of Tiny: tinyIndices: seq[array[3, uint8]]
@@ -51,11 +50,11 @@
   result.vertexCount = uint32(positions.len)
   result.indicesCount = uint32(indices.len * 3)
   result.instanceCount = instances
-  result.vertexdata["position"] = DataList(thetype: Vec3F32)
-  setValues(result.vertexdata["position"], positions.toSeq)
+  result.data["position"] = DataList(thetype: Vec3F32)
+  setValues(result.data["position"], positions.toSeq)
   if colors.len > 0:
-    result.vertexdata["color"] = DataList(thetype: Vec3F32)
-    setValues(result.vertexdata["color"], colors.toSeq)
+    result.data["color"] = DataList(thetype: Vec3F32)
+    setValues(result.data["color"], colors.toSeq)
 
   for i in indices:
     assert uint32(i[0]) < result.vertexCount
@@ -85,9 +84,8 @@
 ): auto =
   newMesh(positions, newSeq[array[3, int]](), colors, instances)
 
-func vertexDataSize*(mesh: Mesh): uint32 =
-  for d in mesh.vertexdata.values:
-    result += d.size
+func dataSize*(mesh: Mesh, attribute: string): uint32 =
+  mesh.data[attribute].size
 
 func indexDataSize*(mesh: Mesh): uint32 =
   case mesh.indexType
@@ -96,10 +94,6 @@
     of Small: mesh.smallIndices.len * sizeof(get(genericParams(typeof(mesh.smallIndices)), 0))
     of Big: mesh.bigIndices.len * sizeof(get(genericParams(typeof(mesh.bigIndices)), 0))
 
-func instanceDataSize*(mesh: Mesh): uint32 =
-  for d in mesh.instancedata.values:
-    result += d.size
-
 func rawData[T: seq](value: var T): (pointer, uint32) =
   (pointer(addr(value[0])), uint32(sizeof(get(genericParams(typeof(value)), 0)) * value.len))
 
@@ -110,20 +104,14 @@
     of Small: rawData(mesh.smallIndices)
     of Big: rawData(mesh.bigIndices)
 
-func hasVertexDataFor*(mesh: Mesh, attribute: string): bool =
-  attribute in mesh.vertexdata
+func hasDataFor*(mesh: Mesh, attribute: string): bool =
+  attribute in mesh.data
 
-func hasInstanceDataFor*(mesh: Mesh, attribute: string): bool =
-  attribute in mesh.instancedata
-
-func getRawVertexData*(mesh: Mesh, attribute: string): (pointer, uint32) =
-  mesh.vertexdata[attribute].getRawData()
+func getRawData*(mesh: Mesh, attribute: string): (pointer, uint32) =
+  mesh.data[attribute].getRawData()
 
-func getRawInstanceData*(mesh: Mesh, attribute: string): (pointer, uint32) =
-  mesh.instancedata[attribute].getRawData()
-
-proc setInstanceData*[T: GPUType|int|uint|float](mesh: var Mesh, attribute: string, data: seq[T]) =
+proc setMeshData*[T: GPUType|int|uint|float](mesh: var Mesh, attribute: string, data: seq[T]) =
   assert uint32(data.len) == mesh.instanceCount
-  assert not (attribute in mesh.instancedata)
-  mesh.instancedata[attribute] = DataList(thetype: getDataType[T]())
-  setValues(mesh.instancedata[attribute], data)
+  assert not (attribute in mesh.data)
+  mesh.data[attribute] = DataList(thetype: getDataType[T]())
+  setValues(mesh.data[attribute], data)
--- a/src/semicongine/scene.nim	Mon Apr 10 00:53:16 2023 +0700
+++ b/src/semicongine/scene.nim	Mon Apr 10 20:09:37 2023 +0700
@@ -11,15 +11,13 @@
 
 type
   Drawable* = object
-    buffer*: Buffer # buffer
-    offsets*: seq[uint64] # offsets from buffer
     elementCount*: uint32 # number of vertices or indices
+    bufferOffsets*: Table[MemoryLocation, seq[uint64]] # list of buffers and list of offset for each attribute in that buffer
     instanceCount*: uint32 # number of instance
     case indexed*: bool
     of true:
-      indexBuffer*: Buffer
       indexType*: VkIndexType
-      indexOffset*: uint64
+      indexBufferOffset*: uint64
     of false:
       discard
 
@@ -31,12 +29,14 @@
     name*: string
     root*: Entity
     drawables: Table[VkPipeline, seq[Drawable]]
+    vertexBuffers*: Table[MemoryLocation, Buffer]
+    indexBuffer*: Buffer
 
 func `$`*(drawable: Drawable): string =
   if drawable.indexed:
-    &"Drawable(elementCount: {drawable.elementCount}, instanceCount: {drawable.instanceCount}, buffer: {drawable.buffer}, offsets: {drawable.offsets}, indexType: {drawable.indexType}, indexOffset: {drawable.indexOffset}, indexBuffer: {drawable.indexBuffer})"
+    &"Drawable(elementCount: {drawable.elementCount}, instanceCount: {drawable.instanceCount}, bufferOffsets: {drawable.bufferOffsets}, indexType: {drawable.indexType}, indexBufferOffset: {drawable.indexBufferOffset})"
   else:
-    &"Drawable(elementCount: {drawable.elementCount}, instanceCount: {drawable.instanceCount}, buffer: {drawable.buffer}, offsets: {drawable.offsets})"
+    &"Drawable(elementCount: {drawable.elementCount}, instanceCount: {drawable.instanceCount}, bufferOffsets: {drawable.bufferOffsets})"
 
 func `$`*(global: ShaderGlobal): string =
   &"ShaderGlobal(name: {global.name}, {global.value})"
@@ -46,20 +46,11 @@
   value.setValue(data)
   ShaderGlobal(name: name, value: value)
 
-func 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
-      counted.add drawable.buffer.vk
-    if drawable.indexed and not (drawable.indexBuffer.vk in counted):
-      result.add drawable.indexBuffer
-      counted.add drawable.indexBuffer.vk
-
 proc destroy*(scene: var Scene, pipeline: VkPipeline) =
-  var buffers = scene.getBuffers(pipeline)
-  for buffer in buffers.mitems:
-      buffer.destroy()
+  for buffer in scene.vertexBuffers.mvalues:
+    buffer.destroy()
+  if scene.indexBuffer.vk.valid:
+    scene.indexBuffer.destroy
 
 proc destroy*(scene: var Scene) =
   for pipeline in scene.drawables.keys:
@@ -67,101 +58,89 @@
 
 proc setupDrawables(scene: var Scene, pipeline: Pipeline) =
   assert pipeline.device.vk.valid
+
   if pipeline.vk in scene.drawables:
     for drawable in scene.drawables[pipeline.vk].mitems:
       scene.destroy(pipeline.vk)
   scene.drawables[pipeline.vk] = @[]
 
-  var
-    nonIndexedMeshes: seq[Mesh]
-    tinyIndexedMeshes: seq[Mesh]
-    smallIndexedMeshes: seq[Mesh]
-    bigIndexedMeshes: seq[Mesh]
-    allIndexedMeshes: seq[Mesh]
+  var allMeshes: seq[Mesh]
   for mesh in allComponentsOfType[Mesh](scene.root):
-    for inputAttr in pipeline.inputs.vertexInputs:
-      assert mesh.hasVertexDataFor(inputAttr.name), &"{mesh} missing data for {inputAttr}"
-    case mesh.indexType:
-      of None: nonIndexedMeshes.add mesh
-      of Tiny: tinyIndexedMeshes.add mesh
-      of Small: smallIndexedMeshes.add mesh
-      of Big: bigIndexedMeshes.add mesh
-
-  # ordering meshes this way allows us to ignore value alignment (I think, needs more testing)
-  allIndexedMeshes = bigIndexedMeshes & smallIndexedMeshes & tinyIndexedMeshes
+    allMeshes.add mesh
+    for inputAttr in pipeline.inputs:
+      assert mesh.hasDataFor(inputAttr.name), &"{mesh} missing data for {inputAttr}"
   
-  var
-    indicesBufferSize = 0'u64
-    indexOffset = 0'u64
-  for mesh in allIndexedMeshes:
-    indicesBufferSize += mesh.indexDataSize
-  var indexBuffer: Buffer
+  var indicesBufferSize = 0'u64
+  for mesh in allMeshes:
+    if mesh.indexType != None:
+      let indexAlignment = case mesh.indexType
+        of None: 0'u64
+        of Tiny: 1'u64
+        of Small: 2'u64
+        of Big: 4'u64
+      # index value alignment required by Vulkan
+      if indicesBufferSize mod indexAlignment != 0:
+        indicesBufferSize += indexAlignment - (indicesBufferSize mod indexAlignment)
+      indicesBufferSize += mesh.indexDataSize
   if indicesBufferSize > 0:
-    indexBuffer = pipeline.device.createBuffer(
+    scene.indexBuffer = pipeline.device.createBuffer(
       size=indicesBufferSize,
       usage=[VK_BUFFER_USAGE_INDEX_BUFFER_BIT],
       useVRAM=true,
       mappable=false,
     )
 
-  for location, attributes in pipeline.inputs.vertexInputs.groupByMemoryLocation().pairs:
+  # one vertex data buffer per memory location
+  var perLocationOffsets: Table[MemoryLocation, uint64]
+  for location, attributes in pipeline.inputs.groupByMemoryLocation().pairs:
     # setup one buffer per attribute-location-type
     var bufferSize = 0'u64
-    for mesh in nonIndexedMeshes & allIndexedMeshes:
-      bufferSize += mesh.vertexDataSize
-    if bufferSize == 0:
-      continue
-    var
-      bufferOffset = 0'u64
-      buffer = pipeline.device.createBuffer(
+    for mesh in allMeshes:
+      for attribute in attributes:
+        bufferSize += mesh.dataSize(attribute.name)
+    if bufferSize > 0:
+      scene.vertexBuffers[location] = pipeline.device.createBuffer(
         size=bufferSize,
         usage=[VK_BUFFER_USAGE_VERTEX_BUFFER_BIT],
         useVRAM=location in [VRAM, VRAMVisible],
         mappable=location in [VRAMVisible, RAM],
       )
+      perLocationOffsets[location] = 0
 
-    # TODO: gather instance data/buffers
-    # non-indexed mesh drawable
-    if nonIndexedMeshes.len > 0:
-      var vertexCount = 0'u32
-      for mesh in nonIndexedMeshes:
-        vertexCount += mesh.vertexCount
-      # remark: we merge all meshes into a single drawcall... smart?#
-      # I think bad for instancing...
-      var nonIndexedDrawable = Drawable(
-        elementCount: vertexCount,
-        buffer: buffer,
-        indexed: false,
-        instanceCount: 1
-      )
-      for inputAttr in attributes:
-        nonIndexedDrawable.offsets.add bufferOffset
-        for mesh in nonIndexedMeshes:
-          var (pdata, size) = mesh.getRawVertexData(inputAttr.name)
-          buffer.setData(pdata, size, bufferOffset)
-          bufferOffset += size
-      scene.drawables[pipeline.vk].add nonIndexedDrawable
+  var indexBufferOffset = 0'u64
+  for mesh in allMeshes:
+    var offsets: Table[MemoryLocation, seq[uint64]]
+    for location, attributes in pipeline.inputs.groupByMemoryLocation().pairs:
+      for attribute in attributes:
+        if not (location in offsets):
+          offsets[location] = @[]
+        offsets[location].add perLocationOffsets[location]
+        var (pdata, size) = mesh.getRawData(attribute.name)
+        scene.vertexBuffers[location].setData(pdata, size, perLocationOffsets[location])
+        perLocationOffsets[location] += size
 
-    # indexed mesh drawable
-    for mesh in allIndexedMeshes:
-      var drawable = Drawable(
-        elementCount: mesh.indicesCount,
-        buffer: buffer,
-        indexed: true,
-        indexBuffer: indexBuffer,
-        indexOffset: indexOffset,
-        indexType: mesh.indexType,
-        instanceCount: 1
-      )
+    let indexed = mesh.indexType != None
+    var drawable = Drawable(
+      elementCount: if indexed: mesh.indicesCount else: mesh.vertexCount,
+      bufferOffsets: offsets,
+      instanceCount: mesh.instanceCount,
+      indexed: indexed,
+    )
+    if indexed:
+      let indexAlignment = case mesh.indexType
+        of None: 0'u64
+        of Tiny: 1'u64
+        of Small: 2'u64
+        of Big: 4'u64
+      # index value alignment required by Vulkan
+      if indexBufferOffset mod indexAlignment != 0:
+        indexBufferOffset += indexAlignment - (indexBufferOffset mod indexAlignment)
+      drawable.indexBufferOffset = indexBufferOffset
+      drawable.indexType = mesh.indexType
       var (pdata, size) = mesh.getRawIndexData()
-      indexBuffer.setData(pdata, size, indexOffset)
-      indexOffset += size
-      for inputAttr in attributes:
-        drawable.offsets.add bufferOffset
-        var (pdata, size) = mesh.getRawVertexData(inputAttr.name)
-        buffer.setData(pdata, size, bufferOffset)
-        bufferOffset += size
-      scene.drawables[pipeline.vk].add drawable
+      scene.indexBuffer.setData(pdata, size, indexBufferOffset)
+      indexBufferOffset += size
+    scene.drawables[pipeline.vk].add drawable
 
 proc setupDrawables*(scene: var Scene, renderPass: RenderPass) =
   for subpass in renderPass.subpasses:
--- a/src/semicongine/vulkan/buffer.nim	Mon Apr 10 00:53:16 2023 +0700
+++ b/src/semicongine/vulkan/buffer.nim	Mon Apr 10 20:09:37 2023 +0700
@@ -20,6 +20,9 @@
       of true:
         memory*: DeviceMemory
 
+proc `==`*(a, b: Buffer): bool =
+  a.vk == b.vk
+
 func `$`*(buffer: Buffer): string =
   &"Buffer(vk: {buffer.vk}, size: {buffer.size}, usage: {buffer.usage})"
 
--- a/src/semicongine/vulkan/swapchain.nim	Mon Apr 10 00:53:16 2023 +0700
+++ b/src/semicongine/vulkan/swapchain.nim	Mon Apr 10 20:09:37 2023 +0700
@@ -203,13 +203,23 @@
   commandBuffer.endRenderCommands()
 
 proc draw*(commandBuffer: VkCommandBuffer, drawables: seq[Drawable], scene: Scene) =
+
+  debug "Scene buffers:"
+  for (location, buffer) in scene.vertexBuffers.pairs:
+    echo "  ", location, ": ", buffer
+  echo "  Index buffer: ", scene.indexBuffer
+
   for drawable in drawables:
     debug "Draw ", drawable
+
     var buffers: seq[VkBuffer]
     var offsets: seq[VkDeviceSize]
-    for offset in drawable.offsets:
-      buffers.add drawable.buffer.vk
-      offsets.add VkDeviceSize(offset)
+
+    for (location, bufferOffsets) in drawable.bufferOffsets.pairs:
+      for offset in bufferOffsets:
+        buffers.add scene.vertexBuffers[location].vk
+        offsets.add VkDeviceSize(offset)
+
     commandBuffer.vkCmdBindVertexBuffers(
       firstBinding=0'u32,
       bindingCount=uint32(buffers.len),
@@ -217,7 +227,7 @@
       pOffsets=offsets.toCPointer()
     )
     if drawable.indexed:
-      commandBuffer.vkCmdBindIndexBuffer(drawable.indexBuffer.vk, VkDeviceSize(drawable.indexOffset), drawable.indexType)
+      commandBuffer.vkCmdBindIndexBuffer(scene.indexBuffer.vk, VkDeviceSize(drawable.indexBufferOffset), drawable.indexType)
       commandBuffer.vkCmdDrawIndexed(
         indexCount=drawable.elementCount,
         instanceCount=drawable.instanceCount,
--- a/tests/test_vulkan_wrapper.nim	Mon Apr 10 00:53:16 2023 +0700
+++ b/tests/test_vulkan_wrapper.nim	Mon Apr 10 20:09:37 2023 +0700
@@ -33,65 +33,10 @@
     for format in device.getSurfaceFormats():
       echo "    " & $format
 
-when isMainModule:
-  # INIT ENGINE:
-  # create instance
-  var thewindow = createWindow("Test")
-  var instance = thewindow.createInstance(
-    vulkanVersion=VK_MAKE_API_VERSION(0, 1, 3, 0),
-    instanceExtensions= @["VK_EXT_debug_utils"],
-    layers= @["VK_LAYER_KHRONOS_validation", "VK_LAYER_MESA_overlay"]
-  )
-  var debugger = instance.createDebugMessenger()
-  # create devices
-  let selectedPhysicalDevice = instance.getPhysicalDevices().filterBestGraphics()
-  var device = instance.createDevice(
-    selectedPhysicalDevice,
-    @[],
-    @["VK_EXT_index_type_uint8"],
-    selectedPhysicalDevice.filterForGraphicsPresentationQueues()
-  )
-
-  # INIT RENDERER:
-  const
-    vertexInput = @[
-      attr[Vec3f]("position"),
-      attr[Vec3f]("color"),
-      attr[Vec3f]("translate", 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 * 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);"
-    )
-  var
-    vertexshader = device.createShaderModule(vertexCode)
-    fragmentshader = device.createShaderModule(fragmentCode)
-    surfaceFormat = device.physicalDevice.getSurfaceFormats().filterSurfaceFormat()
-    renderPass = device.simpleForwardRenderPass(surfaceFormat.format, vertexshader, fragmentshader, 2)
-    (swapchain, res) = device.createSwapchain(renderPass, surfaceFormat, device.firstGraphicsQueue().get().family, 2)
-  if res != VK_SUCCESS:
-    raise newException(Exception, "Unable to create swapchain")
-
-  # INIT SCENE
-  var time = initShaderGlobal("time", 0.0'f32)
-  #[
-  var thescene = Scene(
+proc scene_different_mesh_types(): Scene =
+  result = 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)],
@@ -124,27 +69,98 @@
       )),
     )
   )
-  ]#
-  var mymesh = newMesh(
+
+proc scene_simple(): Scene =
+  var mymesh1 = newMesh(
+    positions=[newVec3f(0.0, -0.3), newVec3f(0.3, 0.3), newVec3f(-0.3, 0.3)],
+    colors=[newVec3f(1.0, 0.0, 0.0), newVec3f(0.0, 1.0, 0.0), newVec3f(0.0, 0.0, 1.0)],
+  )
+  var mymesh2 = 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)],
   )
-  setInstanceData[Vec3f](mymesh, "translate", @[newVec3f(0.3, 0.3)])
-  var thescene = Scene(
+  var mymesh3 = newMesh(
+    positions=[newVec3f(0.0, -0.6), newVec3f(0.6, 0.6), newVec3f(-0.6, 0.6)],
+    colors=[newVec3f(1.0, 1.0, 0.0), newVec3f(1.0, 1.0, 0.0), newVec3f(1.0, 1.0, 0.0)],
+    indices=[[0'u32, 1'u32, 2'u32]],
+    autoResize=false
+  )
+  var mymesh4 = newMesh(
+    positions=[newVec3f(0.0, -0.8), newVec3f(0.8, 0.8), newVec3f(-0.8, 0.8)],
+    colors=[newVec3f(0.0, 0.0, 1.0), newVec3f(0.0, 0.0, 1.0), newVec3f(0.0, 0.0, 1.0)],
+    indices=[[0'u16, 1'u16, 2'u16]],
+  )
+  setMeshData[Vec3f](mymesh1, "translate", @[newVec3f(0.3, 0.3)])
+  result = Scene(
     name: "main",
-    root: newEntity("root",
-      newEntity("stuff", time),
-      newEntity("triangle", mymesh),
+    root: newEntity("root", newEntity("triangle", mymesh4, mymesh3, mymesh2, mymesh1),)
+  )
+
+
+when isMainModule:
+  # INIT ENGINE:
+  # create instance
+  var thewindow = createWindow("Test")
+  var instance = thewindow.createInstance(
+    vulkanVersion=VK_MAKE_API_VERSION(0, 1, 3, 0),
+    instanceExtensions= @["VK_EXT_debug_utils"],
+    layers= @["VK_LAYER_KHRONOS_validation", "VK_LAYER_MESA_overlay"]
+  )
+  var debugger = instance.createDebugMessenger()
+  # create devices
+  let selectedPhysicalDevice = instance.getPhysicalDevices().filterBestGraphics()
+  var device = instance.createDevice(
+    selectedPhysicalDevice,
+    @[],
+    @["VK_EXT_index_type_uint8"],
+    selectedPhysicalDevice.filterForGraphicsPresentationQueues()
+  )
+
+  # INIT RENDERER:
+  const
+    vertexInput = @[
+      attr[Vec3f]("position", memoryLocation=VRAM),
+      attr[Vec3f]("color", memoryLocation=VRAM),
+      # attr[Vec3f]("translate", 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 * 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);"
+    )
+  var
+    vertexshader = device.createShaderModule(vertexCode)
+    fragmentshader = device.createShaderModule(fragmentCode)
+    surfaceFormat = device.physicalDevice.getSurfaceFormats().filterSurfaceFormat()
+    renderPass = device.simpleForwardRenderPass(surfaceFormat.format, vertexshader, fragmentshader, 2)
+    (swapchain, res) = device.createSwapchain(renderPass, surfaceFormat, device.firstGraphicsQueue().get().family, 2)
+  if res != VK_SUCCESS:
+    raise newException(Exception, "Unable to create swapchain")
+
+  # INIT SCENE
+  var time = initShaderGlobal("time", 0.0'f32)
+
+  var thescene = scene_simple()
+  thescene.root.components.add time
   thescene.setupDrawables(renderPass)
   swapchain.setupUniforms(thescene)
 
   # MAINLOOP
   echo "Setup successfull, start rendering"
-  for i in 0 ..< 1:
+  for i in 0 ..< 10000:
     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()