changeset 127:5871acc2977e

did: big refactoring
author Sam <sam@basx.dev>
date Wed, 12 Apr 2023 01:20:53 +0700
parents 81a8e62215db
children 9901ec3831d1
files src/semicongine.nim src/semicongine/config.nim src/semicongine/engine.nim src/semicongine/entity.nim src/semicongine/mesh.nim src/semicongine/renderer.nim src/semicongine/scene.nim src/semicongine/vulkan/drawable.nim src/semicongine/vulkan/framebuffer.nim src/semicongine/vulkan/pipeline.nim src/semicongine/vulkan/renderpass.nim src/semicongine/vulkan/shader.nim src/semicongine/vulkan/swapchain.nim tests/test_vulkan_wrapper.nim
diffstat 14 files changed, 330 insertions(+), 276 deletions(-) [+]
line wrap: on
line diff
--- a/src/semicongine.nim	Tue Apr 11 01:06:37 2023 +0700
+++ b/src/semicongine.nim	Wed Apr 12 01:20:53 2023 +0700
@@ -1,5 +1,6 @@
 import semicongine/color
 import semicongine/config
+import semicongine/engine
 import semicongine/entity
 import semicongine/events
 import semicongine/gpu_data
@@ -11,6 +12,7 @@
 
 export color
 export config
+export engine
 export entity
 export events
 export gpu_data
--- a/src/semicongine/config.nim	Tue Apr 11 01:06:37 2023 +0700
+++ b/src/semicongine/config.nim	Wed Apr 12 01:20:53 2023 +0700
@@ -17,7 +17,7 @@
 
 # root of where config files will be searched
 # must be relative (to the directory of the binary)
-const DEBUG = not defined(release)
+const DEBUG* = not defined(release)
 const CONFIGROOT {.strdefine.}: string = "."
 assert not isAbsolute(CONFIGROOT)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/semicongine/engine.nim	Wed Apr 12 01:20:53 2023 +0700
@@ -0,0 +1,49 @@
+import ./platform/window
+
+import ./vulkan/api
+import ./vulkan/instance
+import ./vulkan/device
+import ./vulkan/physicaldevice
+
+import ./config
+
+type
+  Engine* = object
+    device: Device
+    debugger: Debugger
+    instance: Instance
+    window: NativeWindow
+
+func gpuDevice*(engine: Engine): Device =
+  engine.device
+
+proc initEngine*(applicationName: string, debug=DEBUG): Engine =
+  result.window = createWindow(applicationName)
+  var
+    instanceExtensions: seq[string]
+    enabledLayers: seq[string]
+
+  if debug:
+    instanceExtensions.add "VK_EXT_debug_utils"
+    enabledLayers.add @["VK_LAYER_KHRONOS_validation", "VK_LAYER_MESA_overlay"]
+  result.instance = result.window.createInstance(
+    vulkanVersion=VK_MAKE_API_VERSION(0, 1, 3, 0),
+    instanceExtensions=instanceExtensions,
+    layers=enabledLayers,
+  )
+  if debug:
+    result.debugger = result.instance.createDebugMessenger()
+  # create devices
+  let selectedPhysicalDevice = result.instance.getPhysicalDevices().filterBestGraphics()
+  result.device = result.instance.createDevice(
+    selectedPhysicalDevice,
+    enabledLayers = @[],
+    enabledExtensions = @[],
+    selectedPhysicalDevice.filterForGraphicsPresentationQueues()
+  )
+
+proc destroy*(engine: var Engine) =
+  engine.device.destroy()
+  engine.debugger.destroy()
+  engine.instance.destroy()
+  engine.window.destroy()
--- a/src/semicongine/entity.nim	Tue Apr 11 01:06:37 2023 +0700
+++ b/src/semicongine/entity.nim	Wed Apr 12 01:20:53 2023 +0700
@@ -2,6 +2,7 @@
 import std/typetraits
 
 import ./math/matrix
+import ./gpu_data
 
 type
   Component* = ref object of RootObj
@@ -14,6 +15,18 @@
     children*: seq[Entity]
     components*: seq[Component]
 
+  ShaderGlobal* = ref object of Component
+    name*: string
+    value*: DataValue
+
+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)
+
 
 method `$`*(entity: Entity): string {.base.} = entity.name
 method `$`*(component: Component): string {.base.} =
--- a/src/semicongine/mesh.nim	Tue Apr 11 01:06:37 2023 +0700
+++ b/src/semicongine/mesh.nim	Wed Apr 12 01:20:53 2023 +0700
@@ -169,7 +169,6 @@
   var
     pos = @[newVec3f(0, 0), newVec3f(0, half_h)]
     col = @[c, c]
-    index: seq[array[3, uint16]] = @[]
   for i in 0'u16 .. nSegments:
     pos.add newVec3f(cos(float32(i) * step) * half_w, sin(float32(i) * step) * half_h)
     col.add c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/semicongine/renderer.nim	Wed Apr 12 01:20:53 2023 +0700
@@ -0,0 +1,27 @@
+import std/options
+
+import ./vulkan/api
+import ./vulkan/device
+import ./vulkan/physicaldevice
+import ./vulkan/renderpass
+import ./vulkan/swapchain
+
+type
+  Renderer = object
+    surfaceFormat: VkSurfaceFormatKHR
+    renderPasses: seq[RenderPass]
+    swapchain: Swapchain
+
+
+proc initRenderer(device: Device, renderPasses: seq[RenderPass]): Renderer =
+  assert device.vk.valid
+  assert renderPasses.len > 0
+  for renderPass in renderPasses:
+    assert renderPass.vk.valid
+
+  result.renderPasses = renderPasses
+  result.surfaceFormat = device.physicalDevice.getSurfaceFormats().filterSurfaceFormat()
+  let (swapchain, res) = device.createSwapchain(renderPasses[^1], result.surfaceFormat, device.firstGraphicsQueue().get().family, 2)
+  if res != VK_SUCCESS:
+    raise newException(Exception, "Unable to create swapchain")
+  result.swapchain = swapchain
--- a/src/semicongine/scene.nim	Tue Apr 11 01:06:37 2023 +0700
+++ b/src/semicongine/scene.nim	Wed Apr 12 01:20:53 2023 +0700
@@ -3,71 +3,27 @@
 
 import ./vulkan/api
 import ./vulkan/buffer
-import ./vulkan/pipeline
-import ./vulkan/renderpass
+import ./vulkan/device
+import ./vulkan/drawable
+
 import ./gpu_data
 import ./entity
 import ./mesh
 
 type
-  Drawable* = object
-    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:
-      indexType*: VkIndexType
-      indexBufferOffset*: uint64
-    of false:
-      discard
-
-  ShaderGlobal* = ref object of Component
-    name*: string
-    value*: DataValue
-
   Scene* = object
     name*: string
     root*: Entity
-    drawables: Table[VkPipeline, seq[Drawable]]
+    drawables*: seq[Drawable]
     vertexBuffers*: Table[MemoryLocation, Buffer]
     indexBuffer*: Buffer
 
-func `$`*(drawable: Drawable): string =
-  if drawable.indexed:
-    &"Drawable(elementCount: {drawable.elementCount}, instanceCount: {drawable.instanceCount}, bufferOffsets: {drawable.bufferOffsets}, indexType: {drawable.indexType}, indexBufferOffset: {drawable.indexBufferOffset})"
-  else:
-    &"Drawable(elementCount: {drawable.elementCount}, instanceCount: {drawable.instanceCount}, bufferOffsets: {drawable.bufferOffsets})"
-
-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 destroy*(scene: var Scene, pipeline: VkPipeline) =
-  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:
-    scene.destroy(pipeline)
-
-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] = @[]
-
+proc setupDrawableBuffers*(scene: var Scene, device: Device, inputs: seq[ShaderAttribute]) =
+  assert scene.drawables.len == 0
   var allMeshes: seq[Mesh]
   for mesh in allComponentsOfType[Mesh](scene.root):
     allMeshes.add mesh
-    for inputAttr in pipeline.inputs:
+    for inputAttr in inputs:
       assert mesh.hasDataFor(inputAttr.name), &"{mesh} missing data for {inputAttr}"
   
   var indicesBufferSize = 0'u64
@@ -83,7 +39,7 @@
         indicesBufferSize += indexAlignment - (indicesBufferSize mod indexAlignment)
       indicesBufferSize += mesh.indexDataSize
   if indicesBufferSize > 0:
-    scene.indexBuffer = pipeline.device.createBuffer(
+    scene.indexBuffer = device.createBuffer(
       size=indicesBufferSize,
       usage=[VK_BUFFER_USAGE_INDEX_BUFFER_BIT],
       useVRAM=true,
@@ -92,14 +48,14 @@
 
   # one vertex data buffer per memory location
   var perLocationOffsets: Table[MemoryLocation, uint64]
-  for location, attributes in pipeline.inputs.groupByMemoryLocation().pairs:
+  for location, attributes in inputs.groupByMemoryLocation().pairs:
     # setup one buffer per attribute-location-type
     var bufferSize = 0'u64
     for mesh in allMeshes:
       for attribute in attributes:
         bufferSize += mesh.dataSize(attribute.name)
     if bufferSize > 0:
-      scene.vertexBuffers[location] = pipeline.device.createBuffer(
+      scene.vertexBuffers[location] = device.createBuffer(
         size=bufferSize,
         usage=[VK_BUFFER_USAGE_VERTEX_BUFFER_BIT],
         useVRAM=location in [VRAM, VRAMVisible],
@@ -110,7 +66,7 @@
   var indexBufferOffset = 0'u64
   for mesh in allMeshes:
     var offsets: Table[MemoryLocation, seq[uint64]]
-    for location, attributes in pipeline.inputs.groupByMemoryLocation().pairs:
+    for location, attributes in inputs.groupByMemoryLocation().pairs:
       for attribute in attributes:
         if not (location in offsets):
           offsets[location] = @[]
@@ -140,12 +96,11 @@
       var (pdata, size) = mesh.getRawIndexData()
       scene.indexBuffer.setData(pdata, size, indexBufferOffset)
       indexBufferOffset += size
-    scene.drawables[pipeline.vk].add drawable
+    scene.drawables.add drawable
 
-proc setupDrawables*(scene: var Scene, renderPass: RenderPass) =
-  for subpass in renderPass.subpasses:
-    for pipeline in subpass.pipelines:
-      scene.setupDrawables(pipeline)
+proc destroy*(scene: var Scene) =
+  for buffer in scene.vertexBuffers.mvalues:
+    buffer.destroy()
+  if scene.indexBuffer.vk.valid:
+    scene.indexBuffer.destroy
 
-func getDrawables*(scene: Scene, pipeline: Pipeline): seq[Drawable] =
-  scene.drawables.getOrDefault(pipeline.vk, @[])
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/semicongine/vulkan/drawable.nim	Wed Apr 12 01:20:53 2023 +0700
@@ -0,0 +1,61 @@
+import std/tables
+import std/strformat
+import std/logging
+
+import ./api
+import ./utils
+import ./buffer
+
+import ../gpu_data
+
+type
+  Drawable* = object
+    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:
+      indexType*: VkIndexType
+      indexBufferOffset*: uint64
+    of false:
+      discard
+
+func `$`*(drawable: Drawable): string =
+  if drawable.indexed:
+    &"Drawable(elementCount: {drawable.elementCount}, instanceCount: {drawable.instanceCount}, bufferOffsets: {drawable.bufferOffsets}, indexType: {drawable.indexType}, indexBufferOffset: {drawable.indexBufferOffset})"
+  else:
+    &"Drawable(elementCount: {drawable.elementCount}, instanceCount: {drawable.instanceCount}, bufferOffsets: {drawable.bufferOffsets})"
+
+proc draw*(commandBuffer: VkCommandBuffer, drawable: Drawable, vertexBuffers: Table[MemoryLocation, Buffer], indexBuffer: BUffer) =
+    debug "Draw ", drawable
+
+    var buffers: seq[VkBuffer]
+    var offsets: seq[VkDeviceSize]
+
+    for (location, bufferOffsets) in drawable.bufferOffsets.pairs:
+      for offset in bufferOffsets:
+        buffers.add vertexBuffers[location].vk
+        offsets.add VkDeviceSize(offset)
+
+    commandBuffer.vkCmdBindVertexBuffers(
+      firstBinding=0'u32,
+      bindingCount=uint32(buffers.len),
+      pBuffers=buffers.toCPointer(),
+      pOffsets=offsets.toCPointer()
+    )
+    if drawable.indexed:
+      commandBuffer.vkCmdBindIndexBuffer(indexBuffer.vk, VkDeviceSize(drawable.indexBufferOffset), drawable.indexType)
+      commandBuffer.vkCmdDrawIndexed(
+        indexCount=drawable.elementCount,
+        instanceCount=drawable.instanceCount,
+        firstIndex=0,
+        vertexOffset=0,
+        firstInstance=0
+      )
+    else:
+      commandBuffer.vkCmdDraw(
+        vertexCount=drawable.elementCount,
+        instanceCount=drawable.instanceCount,
+        firstVertex=0,
+        firstInstance=0
+      )
--- a/src/semicongine/vulkan/framebuffer.nim	Tue Apr 11 01:06:37 2023 +0700
+++ b/src/semicongine/vulkan/framebuffer.nim	Wed Apr 12 01:20:53 2023 +0700
@@ -2,7 +2,6 @@
 import ./device
 import ./utils
 import ./image
-import ./renderpass
 
 import ../math
 
@@ -12,9 +11,9 @@
     vk*: VkFramebuffer
     dimension*: Vec2I
 
-proc createFramebuffer*(device: Device, renderPass: RenderPass, attachments: openArray[ImageView], dimension: Vec2I): Framebuffer =
+proc createFramebuffer*(device: Device, renderpass: VkRenderPass, attachments: openArray[ImageView], dimension: Vec2I): Framebuffer =
   assert device.vk.valid
-  assert renderpass.vk.valid
+  assert renderpass.valid
 
   result.device = device
   result.dimension = dimension
@@ -25,7 +24,7 @@
     theattachments.add a.vk
   var framebufferInfo = VkFramebufferCreateInfo(
     sType: VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
-    renderPass: renderPass.vk,
+    renderPass: renderpass,
     attachmentCount: uint32(theattachments.len),
     pAttachments: theattachments.toCPointer,
     width: dimension[0],
--- a/src/semicongine/vulkan/pipeline.nim	Tue Apr 11 01:06:37 2023 +0700
+++ b/src/semicongine/vulkan/pipeline.nim	Wed Apr 12 01:20:53 2023 +0700
@@ -5,8 +5,10 @@
 import ./device
 import ./descriptor
 import ./shader
+import ./buffer
 import ./utils
 
+import ../entity
 import ../gpu_data
 
 type
@@ -18,6 +20,7 @@
     descriptorSetLayout*: DescriptorSetLayout
     descriptorPool*: DescriptorPool
     descriptorSets*: seq[DescriptorSet]
+    uniformBuffers: seq[Buffer]
 
 func inputs*(pipeline: Pipeline): seq[ShaderAttribute] =
   for shader in pipeline.shaders:
@@ -34,6 +37,23 @@
         uniformList[attribute.name] = attribute
   result = uniformList.values.toSeq
 
+proc setupUniforms(pipeline: var Pipeline, inFlightFrames: int) =
+  assert pipeline.vk.valid
+
+  var uniformBufferSize = 0'u64
+  for uniform in pipeline.uniforms:
+    uniformBufferSize += uniform.thetype.size
+
+  for i in 0 ..< inFlightFrames:
+    var buffer = pipeline.device.createBuffer(
+      size=uniformBufferSize,
+      usage=[VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT],
+      useVRAM=true,
+      mappable=true,
+    )
+    pipeline.uniformBuffers.add buffer
+    pipeline.descriptorSets[i].setDescriptorSet(buffer)
+
 proc createPipeline*(device: Device, renderPass: VkRenderPass, vertexShader: Shader, fragmentShader: Shader, inFlightFrames: int, subpass = 0'u32): Pipeline =
   assert renderPass.valid
   assert device.vk.valid
@@ -45,7 +65,6 @@
   result.device = device
   result.shaders = @[vertexShader, fragmentShader]
   
-  # TODO: correct descriptors over all shaders
   var descriptors = @[
     Descriptor(
       thetype: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
@@ -161,12 +180,33 @@
   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
+  result.setupUniforms(inFlightFrames=inFlightFrames)
+
+proc updateUniforms*(pipeline: Pipeline, rootEntity: Entity, currentInFlight: int) =
+  assert pipeline.vk.valid
+  assert pipeline.uniformBuffers[currentInFlight].vk.valid
+
+  var globalsByName: Table[string, DataValue]
+  for component in allComponentsOfType[ShaderGlobal](rootEntity):
+    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()
+    pipeline.uniformBuffers[currentInFlight].setData(pdata, size, offset)
+    offset += size
+
 
 proc destroy*(pipeline: var Pipeline) =
   assert pipeline.device.vk.valid
   assert pipeline.vk.valid
   assert pipeline.layout.valid
   assert pipeline.descriptorSetLayout.vk.valid
+
+  for buffer in pipeline.uniformBuffers.mitems:
+    assert buffer.vk.valid
+    buffer.destroy()
   
   if pipeline.descriptorPool.vk.valid:
     pipeline.descriptorPool.destroy()
--- a/src/semicongine/vulkan/renderpass.nim	Tue Apr 11 01:06:37 2023 +0700
+++ b/src/semicongine/vulkan/renderpass.nim	Wed Apr 12 01:20:53 2023 +0700
@@ -1,12 +1,19 @@
 import std/options
+import std/tables
+import std/logging
 
 import ./api
 import ./utils
 import ./device
 import ./pipeline
 import ./shader
+import ./drawable
+import ./buffer
+import ./framebuffer
 
 import ../math
+import ../entity
+import ../gpu_data
 
 type
   Subpass* = object
@@ -22,7 +29,6 @@
   RenderPass* = object
     vk*: VkRenderPass
     device*: Device
-    inFlightFrames*: int
     subpasses*: seq[Subpass]
 
 proc createRenderPass*(
@@ -30,7 +36,6 @@
   attachments: seq[VkAttachmentDescription],
   subpasses: seq[Subpass],
   dependencies: seq[VkSubpassDependency],
-  inFlightFrames: int,
 ): RenderPass =
   assert device.vk.valid
   var pAttachments = attachments
@@ -62,7 +67,6 @@
       pDependencies: pDependencies.toCPointer,
     )
   result.device = device
-  result.inFlightFrames = inFlightFrames
   result.subpasses = pSubpasses
   checkVkResult device.vk.vkCreateRenderPass(addr(createInfo), nil, addr(result.vk))
 
@@ -95,9 +99,89 @@
       dstStageMask: toBits [VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT],
       dstAccessMask: toBits [VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT],
     )]
-  result = device.createRenderPass(attachments=attachments, subpasses=subpasses, dependencies=dependencies, inFlightFrames=inFlightFrames)
+  result = device.createRenderPass(attachments=attachments, subpasses=subpasses, dependencies=dependencies)
   result.subpasses[0].pipelines.add device.createPipeline(result.vk, vertexShader, fragmentShader, inFlightFrames, 0)
 
+proc beginRenderCommands*(commandBuffer: VkCommandBuffer, renderpass: RenderPass, framebuffer: Framebuffer) =
+  assert commandBuffer.valid
+  assert renderpass.vk.valid
+  assert framebuffer.vk.valid
+  let
+    w = framebuffer.dimension.x
+    h = framebuffer.dimension.y
+
+  var clearColors: seq[VkClearValue]
+  for subpass in renderpass.subpasses:
+    clearColors.add(VkClearValue(color: VkClearColorValue(float32: subpass.clearColor)))
+  var
+    beginInfo = VkCommandBufferBeginInfo(
+      sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
+      pInheritanceInfo: nil,
+    )
+    renderPassInfo = VkRenderPassBeginInfo(
+      sType: VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
+      renderPass: renderPass.vk,
+      framebuffer: framebuffer.vk,
+      renderArea: VkRect2D(
+        offset: VkOffset2D(x: 0, y: 0),
+        extent: VkExtent2D(width: w, height: h),
+      ),
+      clearValueCount: uint32(clearColors.len),
+      pClearValues: clearColors.toCPointer(),
+    )
+    viewport = VkViewport(
+      x: 0.0,
+      y: 0.0,
+      width: (float)w,
+      height: (float)h,
+      minDepth: 0.0,
+      maxDepth: 1.0,
+    )
+    scissor = VkRect2D(
+      offset: VkOffset2D(x: 0, y: 0),
+      extent: VkExtent2D(width: w, height: h)
+    )
+  checkVkResult commandBuffer.vkResetCommandBuffer(VkCommandBufferResetFlags(0))
+  checkVkResult commandBuffer.vkBeginCommandBuffer(addr(beginInfo))
+  commandBuffer.vkCmdBeginRenderPass(addr(renderPassInfo), VK_SUBPASS_CONTENTS_INLINE)
+  commandBuffer.vkCmdSetViewport(firstViewport=0, viewportCount=1, addr(viewport))
+  commandBuffer.vkCmdSetScissor(firstScissor=0, scissorCount=1, addr(scissor))
+
+proc endRenderCommands*(commandBuffer: VkCommandBuffer) =
+  commandBuffer.vkCmdEndRenderPass()
+  checkVkResult commandBuffer.vkEndCommandBuffer()
+
+proc draw*(
+  commandBuffer: VkCommandBuffer,
+  renderPass: var RenderPass,
+  framebuffer: Framebuffer,
+  rootEntity: Entity,
+  drawables: seq[Drawable],
+  vertexBuffers: Table[MemoryLocation, Buffer],
+  indexBuffer: Buffer,
+  currentInFlight: int
+) =
+  commandBuffer.beginRenderCommands(renderpass, framebuffer)
+  for i in 0 ..< renderpass.subpasses.len:
+    let subpass = renderpass.subpasses[i]
+    for pipeline in subpass.pipelines:
+      var mpipeline = pipeline
+      commandBuffer.vkCmdBindPipeline(subpass.pipelineBindPoint, mpipeline.vk)
+      commandBuffer.vkCmdBindDescriptorSets(subpass.pipelineBindPoint, mpipeline.layout, 0, 1, addr(mpipeline.descriptorSets[currentInFlight].vk), 0, nil)
+      mpipeline.updateUniforms(rootEntity, currentInFlight)
+
+      debug "Scene buffers:"
+      for (location, buffer) in vertexBuffers.pairs:
+        echo "  ", location, ": ", buffer
+      echo "  Index buffer: ", indexBuffer
+
+      for drawable in drawables:
+        commandBuffer.draw(drawable, vertexBuffers=vertexBuffers, indexBuffer=indexBuffer)
+
+    if i < renderpass.subpasses.len - 1:
+      commandBuffer.vkCmdNextSubpass(VK_SUBPASS_CONTENTS_INLINE)
+  commandBuffer.endRenderCommands()
+
 proc destroy*(renderPass: var RenderPass) =
   assert renderPass.device.vk.valid
   assert renderPass.vk.valid
--- a/src/semicongine/vulkan/shader.nim	Tue Apr 11 01:06:37 2023 +0700
+++ b/src/semicongine/vulkan/shader.nim	Wed Apr 12 01:20:53 2023 +0700
@@ -91,14 +91,14 @@
   outputs: seq[ShaderAttribute]= @[],
   version=DEFAULT_SHADER_VERSION ,
   entrypoint=DEFAULT_SHADER_ENTRYPOINT ,
-  body: seq[string]
+  main: seq[string]
 ): ShaderCode {.compileTime.} =
   var code = @[&"#version {version}", ""] &
     (if inputs.len > 0: inputs.glslInput() & @[""] else: @[]) &
     (if uniforms.len > 0: uniforms.glslUniforms() & @[""] else: @[]) &
     (if outputs.len > 0: outputs.glslOutput() & @[""] else: @[]) &
     @[&"void {entrypoint}(){{"] &
-    body &
+    main &
     @[&"}}"]
   result.inputs = inputs
   result.uniforms = uniforms
@@ -115,9 +115,9 @@
   outputs: seq[ShaderAttribute]= @[],
   version=DEFAULT_SHADER_VERSION ,
   entrypoint=DEFAULT_SHADER_ENTRYPOINT ,
-  body: string
+  main: string
 ): ShaderCode {.compileTime.} =
-  return compileGlslShader(stage, inputs, uniforms, outputs, version, entrypoint, @[body])
+  return compileGlslShader(stage, inputs, uniforms, outputs, version, entrypoint, @[main])
 
 
 proc createShaderModule*(
--- a/src/semicongine/vulkan/swapchain.nim	Tue Apr 11 01:06:37 2023 +0700
+++ b/src/semicongine/vulkan/swapchain.nim	Wed Apr 12 01:20:53 2023 +0700
@@ -1,4 +1,3 @@
-import std/tables
 import std/options
 import std/logging
 
@@ -7,21 +6,16 @@
 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
-  Swapchain = object
+  Swapchain* = object
     device*: Device
     vk*: VkSwapchainKHR
     format*: VkFormat
@@ -36,7 +30,7 @@
     imageAvailableSemaphore*: seq[Semaphore]
     renderFinishedSemaphore*: seq[Semaphore]
     commandBufferPool: CommandBufferPool
-    uniformBuffers: Table[VkPipeline, seq[Buffer]]
+    inFlightFrames: int
 
 
 proc createSwapchain*(
@@ -45,12 +39,13 @@
   surfaceFormat: VkSurfaceFormatKHR,
   queueFamily: QueueFamily,
   desiredNumberOfImages=3'u32,
-  presentationMode: VkPresentModeKHR=VK_PRESENT_MODE_MAILBOX_KHR
+  presentationMode: VkPresentModeKHR=VK_PRESENT_MODE_MAILBOX_KHR,
+  inFlightFrames=2
 ): (Swapchain, VkResult) =
   assert device.vk.valid
   assert device.physicalDevice.vk.valid
   assert renderPass.vk.valid
-  assert renderPass.inFlightFrames > 0
+  assert inFlightFrames > 0
 
   var capabilities = device.physicalDevice.getSurfaceCapabilities()
 
@@ -85,6 +80,7 @@
       format: surfaceFormat.format,
       dimension: TVec2[uint32]([capabilities.currentExtent.width, capabilities.currentExtent.height]),
       renderPass: renderPass,
+      inFlightFrames: inFlightFrames
     )
     createResult = device.vk.vkCreateSwapchainKHR(addr(createInfo), nil, addr(swapchain.vk))
 
@@ -98,150 +94,15 @@
       let image = Image(vk: vkimage, format: surfaceFormat.format, device: device)
       let imageview = image.createImageView()
       swapChain.imageviews.add imageview
-      swapChain.framebuffers.add swapchain.device.createFramebuffer(renderPass, [imageview], swapchain.dimension)
-    for i in 0 ..< swapchain.renderPass.inFlightFrames:
+      swapChain.framebuffers.add swapchain.device.createFramebuffer(renderPass.vk, [imageview], swapchain.dimension)
+    for i in 0 ..< swapchain.inFlightFrames:
       swapchain.queueFinishedFence.add device.createFence()
       swapchain.imageAvailableSemaphore.add device.createSemaphore()
       swapchain.renderFinishedSemaphore.add device.createSemaphore()
-    swapchain.commandBufferPool = device.createCommandBufferPool(queueFamily, swapchain.renderPass.inFlightFrames)
+    swapchain.commandBufferPool = device.createCommandBufferPool(queueFamily, swapchain.inFlightFrames)
 
   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
-  assert framebuffer.vk.valid
-  let
-    w = framebuffer.dimension.x
-    h = framebuffer.dimension.y
-
-  var clearColors: seq[VkClearValue]
-  for subpass in renderpass.subpasses:
-    clearColors.add(VkClearValue(color: VkClearColorValue(float32: subpass.clearColor)))
-  var
-    beginInfo = VkCommandBufferBeginInfo(
-      sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
-      pInheritanceInfo: nil,
-    )
-    renderPassInfo = VkRenderPassBeginInfo(
-      sType: VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
-      renderPass: renderPass.vk,
-      framebuffer: framebuffer.vk,
-      renderArea: VkRect2D(
-        offset: VkOffset2D(x: 0, y: 0),
-        extent: VkExtent2D(width: w, height: h),
-      ),
-      clearValueCount: uint32(clearColors.len),
-      pClearValues: clearColors.toCPointer(),
-    )
-    viewport = VkViewport(
-      x: 0.0,
-      y: 0.0,
-      width: (float)w,
-      height: (float)h,
-      minDepth: 0.0,
-      maxDepth: 1.0,
-    )
-    scissor = VkRect2D(
-      offset: VkOffset2D(x: 0, y: 0),
-      extent: VkExtent2D(width: w, height: h)
-    )
-  checkVkResult commandBuffer.vkResetCommandBuffer(VkCommandBufferResetFlags(0))
-  checkVkResult commandBuffer.vkBeginCommandBuffer(addr(beginInfo))
-  commandBuffer.vkCmdBeginRenderPass(addr(renderPassInfo), VK_SUBPASS_CONTENTS_INLINE)
-  commandBuffer.vkCmdSetViewport(firstViewport=0, viewportCount=1, addr(viewport))
-  commandBuffer.vkCmdSetScissor(firstScissor=0, scissorCount=1, addr(scissor))
-
-proc endRenderCommands*(commandBuffer: VkCommandBuffer) =
-  commandBuffer.vkCmdEndRenderPass()
-  checkVkResult commandBuffer.vkEndCommandBuffer()
-
-template renderCommands*(commandBuffer: VkCommandBuffer, renderpass: RenderPass, framebuffer: Framebuffer, body: untyped) =
-  commandBuffer.beginRenderCommands(renderpass, framebuffer)
-  body
-  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 (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),
-      pBuffers=buffers.toCPointer(),
-      pOffsets=offsets.toCPointer()
-    )
-    if drawable.indexed:
-      commandBuffer.vkCmdBindIndexBuffer(scene.indexBuffer.vk, VkDeviceSize(drawable.indexBufferOffset), drawable.indexType)
-      commandBuffer.vkCmdDrawIndexed(
-        indexCount=drawable.elementCount,
-        instanceCount=drawable.instanceCount,
-        firstIndex=0,
-        vertexOffset=0,
-        firstInstance=0
-      )
-    else:
-      commandBuffer.vkCmdDraw(
-        vertexCount=drawable.elementCount,
-        instanceCount=drawable.instanceCount,
-        firstVertex=0,
-        firstInstance=0
-      )
 
 proc drawScene*(swapchain: var Swapchain, scene: Scene): bool =
   assert swapchain.device.vk.valid
@@ -249,7 +110,7 @@
   assert swapchain.device.firstGraphicsQueue().isSome
   assert swapchain.device.firstPresentationQueue().isSome
 
-  swapchain.currentInFlight = (swapchain.currentInFlight + 1) mod swapchain.renderPass.inFlightFrames
+  swapchain.currentInFlight = (swapchain.currentInFlight + 1) mod swapchain.inFlightFrames
   swapchain.queueFinishedFence[swapchain.currentInFlight].wait()
 
   var currentFramebufferIndex: uint32
@@ -267,20 +128,7 @@
   swapchain.queueFinishedFence[swapchain.currentInFlight].reset()
 
   var commandBuffer = swapchain.commandBufferPool.buffers[swapchain.currentInFlight]
-
-  renderCommands(
-    commandBuffer,
-    swapchain.renderpass,
-    swapchain.framebuffers[currentFramebufferIndex]
-  ):
-    for i in 0 ..< swapchain.renderpass.subpasses.len:
-      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)
+  commandBuffer.draw(renderPass=swapchain.renderPass, framebuffer=swapchain.framebuffers[currentFramebufferIndex], rootEntity=scene.root, drawables=scene.drawables, vertexBuffers=scene.vertexBuffers, indexBuffer=scene.indexBuffer, currentInFlight=swapchain.currentInFlight)
 
   var
     waitSemaphores = [swapchain.imageAvailableSemaphore[swapchain.currentInFlight].vk]
@@ -331,16 +179,13 @@
     assert framebuffer.vk.valid
     framebuffer.destroy()
   swapchain.commandBufferPool.destroy()
-  for i in 0 ..< swapchain.renderPass.inFlightFrames:
+  for i in 0 ..< swapchain.inFlightFrames:
     assert swapchain.queueFinishedFence[i].vk.valid
     assert swapchain.imageAvailableSemaphore[i].vk.valid
     assert swapchain.renderFinishedSemaphore[i].vk.valid
     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	Tue Apr 11 01:06:37 2023 +0700
+++ b/tests/test_vulkan_wrapper.nim	Wed Apr 12 01:20:53 2023 +0700
@@ -113,23 +113,7 @@
   )
 
 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()
-  )
+  var engine = initEngine("Test")
 
   # INIT RENDERER:
   const
@@ -146,33 +130,32 @@
       inputs=vertexInput,
       uniforms=uniforms,
       outputs=vertexOutput,
-      body="""gl_Position = vec4(position + translate, 1.0); outcolor = color;"""
+      main="""gl_Position = vec4(position + translate, 1.0); outcolor = color;"""
     )
     fragmentCode = compileGlslShader(
       stage=VK_SHADER_STAGE_FRAGMENT_BIT,
       inputs=vertexOutput,
       uniforms=uniforms,
       outputs=fragOutput,
-      body="color = vec4(outcolor, 1);"
+      main="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)
+    vertexshader = engine.gpuDevice.createShaderModule(vertexCode)
+    fragmentshader = engine.gpuDevice.createShaderModule(fragmentCode)
+    surfaceFormat = engine.gpuDevice.physicalDevice.getSurfaceFormats().filterSurfaceFormat()
+    renderPass = engine.gpuDevice.simpleForwardRenderPass(surfaceFormat.format, vertexshader, fragmentshader, 2)
+    (swapchain, res) = engine.gpuDevice.createSwapchain(renderPass, surfaceFormat, engine.gpuDevice.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()
   # var thescene = scene_different_mesh_types()
   var thescene = scene_primitives()
+  var time = initShaderGlobal("time", 0.0'f32)
   thescene.root.components.add time
-  thescene.setupDrawables(renderPass)
-  swapchain.setupUniforms(thescene)
+  thescene.setupDrawableBuffers(engine.gpuDevice, vertexInput)
 
   # MAINLOOP
   echo "Setup successfull, start rendering"
@@ -180,10 +163,10 @@
     setValue[float32](time.value, get[float32](time.value) + 0.0005)
     discard swapchain.drawScene(thescene)
   echo "Rendered ", swapchain.framesRendered, " frames"
-  checkVkResult device.vk.vkDeviceWaitIdle()
 
   # cleanup
   echo "Start cleanup"
+  checkVkResult engine.gpuDevice.vk.vkDeviceWaitIdle()
 
   # destroy scene
   thescene.destroy()
@@ -195,7 +178,4 @@
   swapchain.destroy()
 
   # destroy engine
-  device.destroy()
-  debugger.destroy()
-  instance.destroy()
-  thewindow.destroy()
+  engine.destroy()