changeset 782:b6950ea89b37

did next step in renderpipeline-refactoring, using shaderconfiguration objects instead for less ambigious shader-pipeline configuration
author Sam <sam@basx.dev>
date Tue, 15 Aug 2023 23:51:37 +0700
parents e45c2927ecca
children 893ec0fbfd44
files src/semicongine/core/gpu_data.nim src/semicongine/engine.nim src/semicongine/renderer.nim src/semicongine/scene.nim src/semicongine/vulkan/drawable.nim src/semicongine/vulkan/pipeline.nim src/semicongine/vulkan/renderpass.nim src/semicongine/vulkan/shader.nim tests/test_vulkan_wrapper.nim
diffstat 9 files changed, 93 insertions(+), 228 deletions(-) [+]
line wrap: on
line diff
--- a/src/semicongine/core/gpu_data.nim	Sun Aug 13 19:00:11 2023 +0700
+++ b/src/semicongine/core/gpu_data.nim	Tue Aug 15 23:51:37 2023 +0700
@@ -1056,7 +1056,7 @@
     of Mat4F64: "dmat4"
     of Sampler2D: "sampler2D"
 
-func glslInput*(group: seq[ShaderAttribute]): seq[string] =
+func glslInput*(group: openArray[ShaderAttribute]): seq[string] =
   if group.len == 0:
     return @[]
   var i = 0'u32
@@ -1067,7 +1067,7 @@
     for j in 0 ..< attribute.thetype.numberOfVertexInputAttributeDescriptors:
       i += attribute.thetype.nLocationSlots
 
-func glslUniforms*(group: seq[ShaderAttribute], blockName="Uniforms", binding: int): seq[string] =
+func glslUniforms*(group: openArray[ShaderAttribute], blockName="Uniforms", binding: int): seq[string] =
   if group.len == 0:
     return @[]
   # currently only a single uniform block supported, therefore binding = 0
@@ -1079,7 +1079,7 @@
     result.add(&"    {attribute.thetype.glslType} {attribute.name}{arrayDecl};")
   result.add(&"}} {blockName};")
 
-func glslSamplers*(group: seq[ShaderAttribute], basebinding: int): seq[string] =
+func glslSamplers*(group: openArray[ShaderAttribute], basebinding: int): seq[string] =
   if group.len == 0:
     return @[]
   var thebinding = basebinding
@@ -1090,7 +1090,7 @@
     result.add(&"layout(binding = {thebinding}) uniform {attribute.thetype.glslType} {attribute.name}{arrayDecl};")
     inc thebinding
 
-func glslOutput*(group: seq[ShaderAttribute]): seq[string] =
+func glslOutput*(group: openArray[ShaderAttribute]): seq[string] =
   if group.len == 0:
     return @[]
   var i = 0'u32
--- a/src/semicongine/engine.nim	Sun Aug 13 19:00:11 2023 +0700
+++ b/src/semicongine/engine.nim	Tue Aug 15 23:51:37 2023 +0700
@@ -1,5 +1,4 @@
 import std/sequtils
-import std/tables
 import std/options
 import std/logging
 import std/os
@@ -112,19 +111,12 @@
   assert transformAttribute == "" or transformAttribute in map(vertexInput, proc(a: ShaderAttribute): string = a.name)
   assert materialIndexAttribute == "" or materialIndexAttribute in map(vertexInput, proc(a: ShaderAttribute): string = a.name)
   assert engine.renderer.isSome
-  engine.renderer.get.setupDrawableBuffers(scene, vertexInput, samplers, transformAttribute=transformAttribute, materialIndexAttribute=materialIndexAttribute)
-
-#[
-proc addScene*(engine: var Engine, scene: Scene, materialShaders: Table[string, ShaderConfiguration], transformAttribute="transform", materialIndexAttribute="materialIndex") =
-  if transformAttribute != "":
-    for shader in materialShaders.values:
-      assert transformAttribute in map(shader.inputs, proc(a: ShaderAttribute): string = a.name)
-  if materialIndexAttribute != "":
-    for shader in materialShaders.values:
-      assert materialIndexAttribute in map(shader.inputs, proc(a: ShaderAttribute): string = a.name)
-  assert engine.renderer.isSome
-  engine.renderer.get.setupDrawableBuffers(scene, vertexInput, samplers, transformAttribute=transformAttribute, materialIndexAttribute=materialIndexAttribute)
-  ]#
+  # TODO:
+  # rethink when and how we set up those buffers
+  # scene should have no idea about shader inputs and samplers, but we need to use those in the setup
+  # idea: gather material-names -> get materials -> get shaders -> determine vertexInputs and samplers?
+  # also, be aware: need to support multiple pipelines/shaders
+  engine.renderer.get.setupDrawableBuffers(scene, vertexInput, samplers)
 
 proc renderScene*(engine: var Engine, scene: var Scene) =
   assert engine.state == Running
--- a/src/semicongine/renderer.nim	Sun Aug 13 19:00:11 2023 +0700
+++ b/src/semicongine/renderer.nim	Tue Aug 15 23:51:37 2023 +0700
@@ -56,31 +56,31 @@
     raise newException(Exception, "Unable to create swapchain")
   result.swapchain = swapchain.get()
 
-proc setupDrawableBuffers*(renderer: var Renderer, scene: Scene, inputs: seq[ShaderAttribute], samplers: seq[ShaderAttribute], transformAttribute="transform", materialIndexAttribute="materialIndex") =
+proc setupDrawableBuffers*(renderer: var Renderer, scene: Scene, inputs: seq[ShaderAttribute], samplers: seq[ShaderAttribute]) =
   assert not (scene in renderer.scenedata)
   const VERTEX_ATTRIB_ALIGNMENT = 4 # used for buffer alignment
   var scenedata = SceneData()
 
   # if mesh transformation are handled through the scenegraph-transformation, set it up here
-  if transformattribute != "":
+  if scene.transformAttribute != "":
     var hasTransformAttribute = false
     for input in inputs:
-      if input.name == transformattribute:
+      if input.name == scene.transformAttribute:
         assert input.perInstance == true, $input
         assert getDataType[Mat4]() == input.thetype
         hasTransformAttribute = true
     assert hasTransformAttribute
-    scenedata.transformAttribute = transformAttribute
+    scenedata.transformAttribute = scene.transformAttribute
 
   # check if we have support for material indices, if required
-  if materialIndexAttribute != "":
+  if scene.materialIndexAttribute != "":
     var hasMaterialIndexAttribute = false
     for input in inputs:
-      if input.name == materialIndexAttribute:
+      if input.name == scene.materialIndexAttribute:
         assert getDataType[uint16]() == input.thetype
         hasMaterialIndexAttribute = true
     assert hasMaterialIndexAttribute
-    scenedata.materialIndexAttribute = materialIndexAttribute
+    scenedata.materialIndexAttribute = scene.materialIndexAttribute
 
   # find all meshes, populate missing attribute values for shader
   var allMeshes: seq[Mesh]
@@ -312,7 +312,7 @@
       debug "  Index buffer: ", renderer.scenedata[scene].indexBuffer
 
       for drawable in renderer.scenedata[scene].drawables.values:
-        commandBuffer.draw(drawable, vertexBuffers=renderer.scenedata[scene].vertexBuffers, indexBuffer=renderer.scenedata[scene].indexBuffer)
+        drawable.draw(commandBuffer, vertexBuffers=renderer.scenedata[scene].vertexBuffers, indexBuffer=renderer.scenedata[scene].indexBuffer)
 
     if i < renderer.renderPass.subpasses.len - 1:
       commandBuffer.vkCmdNextSubpass(VK_SUBPASS_CONTENTS_INLINE)
--- a/src/semicongine/scene.nim	Sun Aug 13 19:00:11 2023 +0700
+++ b/src/semicongine/scene.nim	Tue Aug 15 23:51:37 2023 +0700
@@ -15,6 +15,8 @@
     root*: Entity
     shaderGlobals*: Table[string, DataList]
     materials: OrderedTable[string, Material]
+    transformAttribute*: string = "transform"
+    materialIndexAttribute*: string = "materialIndex"
 
   Component* = ref object of RootObj
     entity*: Entity
--- a/src/semicongine/vulkan/drawable.nim	Sun Aug 13 19:00:11 2023 +0700
+++ b/src/semicongine/vulkan/drawable.nim	Tue Aug 15 23:51:37 2023 +0700
@@ -26,7 +26,7 @@
   else:
     &"Drawable(elementCount: {drawable.elementCount}, instanceCount: {drawable.instanceCount}, bufferOffsets: {drawable.bufferOffsets})"
 
-proc draw*(commandBuffer: VkCommandBuffer, drawable: Drawable, vertexBuffers: Table[MemoryPerformanceHint, Buffer], indexBuffer: Buffer) =
+proc draw*(drawable: Drawable, commandBuffer: VkCommandBuffer, vertexBuffers: Table[MemoryPerformanceHint, Buffer], indexBuffer: Buffer) =
     if drawable.mesh.entity.transform == Mat4():
       return
     debug "Draw ", drawable
--- a/src/semicongine/vulkan/pipeline.nim	Sun Aug 13 19:00:11 2023 +0700
+++ b/src/semicongine/vulkan/pipeline.nim	Tue Aug 15 23:51:37 2023 +0700
@@ -14,23 +14,14 @@
     device*: Device
     vk*: VkPipeline
     layout*: VkPipelineLayout
-    shaders*: seq[Shader]
+    shaderConfiguration: ShaderConfiguration
+    shaderModules*: (ShaderModule, ShaderModule)
     descriptorSetLayout*: DescriptorSetLayout
 
-func inputs*(pipeline: Pipeline): seq[ShaderAttribute] =
-  for shader in pipeline.shaders:
-    if shader.stage == VK_SHADER_STAGE_VERTEX_BIT:
-      return shader.inputs
+func inputs*(pipeline: Pipeline): seq[ShaderAttribute] = pipeline.shaderConfiguration.inputs
 
 func uniforms*(pipeline: Pipeline): seq[ShaderAttribute] =
-  var visitedUniforms: Table[string, ShaderAttribute]
-  for shader in pipeline.shaders:
-    for attribute in shader.uniforms:
-      if attribute.name in visitedUniforms:
-        assert visitedUniforms[attribute.name] == attribute
-      else:
-        result.add attribute
-        visitedUniforms[attribute.name] = attribute
+  pipeline.shaderConfiguration.uniforms
 
 proc setupDescriptors*(pipeline: var Pipeline, descriptorPool: DescriptorPool, buffers: seq[Buffer], textures: Table[string, seq[VulkanTexture]], inFlightFrames: int): seq[DescriptorSet] =
   assert pipeline.vk.valid
@@ -57,32 +48,23 @@
           descriptor.imageviews.add t.imageView
           descriptor.samplers.add t.sampler
 
-proc createPipeline*(device: Device, renderPass: VkRenderPass, vertexCode: ShaderCode, fragmentCode: ShaderCode, inFlightFrames: int, subpass = 0'u32): Pipeline =
+proc createPipeline*(device: Device, renderPass: VkRenderPass, shaderConfiguration: ShaderConfiguration, inFlightFrames: int, subpass = 0'u32): Pipeline =
   assert renderPass.valid
   assert device.vk.valid
 
-  var
-    vertexShader = device.createShaderModule(vertexCode)
-    fragmentShader = device.createShaderModule(fragmentCode)
-  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
-  assert vertexShader.samplers == fragmentShader.samplers
-
   result.device = device
-  result.shaders = @[vertexShader, fragmentShader]
+  result.shaderModules = device.createShaderModules(shaderConfiguration)
   
   var descriptors: seq[Descriptor]
-  if vertexCode.uniforms.len > 0:
+  if shaderConfiguration.uniforms.len > 0:
     descriptors.add Descriptor(
       name: "Uniforms",
       thetype: Uniform,
       count: 1,
       stages: @[VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_STAGE_FRAGMENT_BIT],
-      size: vertexShader.uniforms.size(),
+      size: shaderConfiguration.uniforms.size(),
     )
-  for sampler in vertexShader.samplers:
+  for sampler in shaderConfiguration.samplers:
     descriptors.add Descriptor(
       name: sampler.name,
       thetype: ImageSampler,
@@ -111,7 +93,7 @@
   var
     bindings: seq[VkVertexInputBindingDescription]
     attributes: seq[VkVertexInputAttributeDescription]
-    vertexInputInfo = vertexShader.getVertexInputInfo(bindings, attributes)
+    vertexInputInfo = shaderConfiguration.getVertexInputInfo(bindings, attributes)
     inputAssembly = VkPipelineInputAssemblyStateCreateInfo(
       sType: VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
       topology: VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
@@ -166,7 +148,7 @@
       dynamicStateCount: uint32(dynamicStates.len),
       pDynamicStates: dynamicStates.toCPointer,
     )
-    stages = @[vertexShader.getPipelineInfo(), fragmentShader.getPipelineInfo()]
+    stages = @[result.shaderModules[0].getPipelineInfo(), result.shaderModules[1].getPipelineInfo()]
     createInfo = VkGraphicsPipelineCreateInfo(
       sType: VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
       stageCount: uint32(stages.len),
@@ -203,8 +185,8 @@
   assert pipeline.layout.valid
   assert pipeline.descriptorSetLayout.vk.valid
 
-  for shader in pipeline.shaders.mitems:
-    shader.destroy()
+  pipeline.shaderModules[0].destroy()
+  pipeline.shaderModules[1].destroy()
   pipeline.descriptorSetLayout.destroy()
   pipeline.device.vk.vkDestroyPipelineLayout(pipeline.layout, nil)
   pipeline.device.vk.vkDestroyPipeline(pipeline.vk, nil)
--- a/src/semicongine/vulkan/renderpass.nim	Sun Aug 13 19:00:11 2023 +0700
+++ b/src/semicongine/vulkan/renderpass.nim	Tue Aug 15 23:51:37 2023 +0700
@@ -64,14 +64,13 @@
 
 proc simpleForwardRenderPass*(
   device: Device,
-  vertexCode: ShaderCode,
-  fragmentCode: ShaderCode,
+  shaderConfiguration: ShaderConfiguration,
   inFlightFrames=2,
   format=VK_FORMAT_UNDEFINED ,
   clearColor=Vec4f([0.8'f32, 0.8'f32, 0.8'f32, 1'f32])
 ): RenderPass =
   assert device.vk.valid
-  assert fragmentCode.outputs.len == 1
+  assert shaderConfiguration.outputs.len == 1
   var theformat = format
   if theformat == VK_FORMAT_UNDEFINED:
     theformat = device.physicalDevice.getSurfaceFormats().filterSurfaceFormat().format
@@ -103,7 +102,7 @@
       dstAccessMask: toBits [VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT],
     )]
   result = device.createRenderPass(attachments=attachments, subpasses=subpasses, dependencies=dependencies)
-  result.subpasses[0].pipelines.add device.createPipeline(result.vk, vertexCode, fragmentCode, inFlightFrames, 0)
+  result.subpasses[0].pipelines.add device.createPipeline(result.vk, shaderConfiguration, inFlightFrames, 0)
 
 proc beginRenderCommands*(commandBuffer: VkCommandBuffer, renderpass: RenderPass, framebuffer: Framebuffer) =
   assert commandBuffer.valid
--- a/src/semicongine/vulkan/shader.nim	Sun Aug 13 19:00:11 2023 +0700
+++ b/src/semicongine/vulkan/shader.nim	Tue Aug 15 23:51:37 2023 +0700
@@ -17,23 +17,11 @@
 addHandler(logger)
 
 type
-  ShaderCode* = object # compiled shader code with some meta data
-    binary: seq[uint32]
-    stage: VkShaderStageFlagBits
-    entrypoint: string
-    inputs*: seq[ShaderAttribute]
-    uniforms*: seq[ShaderAttribute]
-    samplers*: seq[ShaderAttribute]
-    outputs*: seq[ShaderAttribute]
-  Shader* = object
+  ShaderModule* = object
     device: Device
     vk*: VkShaderModule
     stage*: VkShaderStageFlagBits
-    entrypoint*: string
-    inputs*: seq[ShaderAttribute]
-    uniforms*: seq[ShaderAttribute]
-    samplers*: seq[ShaderAttribute]
-    outputs*: seq[ShaderAttribute]
+    configuration*: ShaderConfiguration
   ShaderConfiguration* = object
     vertexBinary: seq[uint32]
     fragmentBinary: seq[uint32]
@@ -94,40 +82,12 @@
     )
     i += 4
 
-
-proc compileGlslShader*(
-  stage: VkShaderStageFlagBits,
-  inputs: seq[ShaderAttribute]= @[],
-  uniforms: seq[ShaderAttribute]= @[],
-  samplers: seq[ShaderAttribute]= @[],
-  outputs: seq[ShaderAttribute]= @[],
-  version=DEFAULT_SHADER_VERSION ,
-  entrypoint=DEFAULT_SHADER_ENTRYPOINT ,
-  main: string
-): ShaderCode {.compileTime.} =
-
-  let code = @[&"#version {version}", "#extension GL_EXT_scalar_block_layout : require", ""] &
-    (if inputs.len > 0: inputs.glslInput() & @[""] else: @[]) &
-    (if uniforms.len > 0: uniforms.glslUniforms(binding=0) & @[""] else: @[]) &
-    (if samplers.len > 0: samplers.glslSamplers(basebinding=if uniforms.len > 0: 1 else: 0) & @[""] else: @[]) &
-    (if outputs.len > 0: outputs.glslOutput() & @[""] else: @[]) &
-    @[&"void {entrypoint}(){{"] &
-    main &
-    @[&"}}"]
-  result.inputs = inputs
-  result.uniforms = uniforms
-  result.samplers = samplers
-  result.outputs = outputs
-  result.entrypoint = entrypoint
-  result.stage = stage
-  result.binary = compileGlslToSPIRV(stage, code.join("\n"), entrypoint)
-
 proc compileGlslCode*(
   stage: VkShaderStageFlagBits,
-  inputs: seq[ShaderAttribute]= @[],
-  uniforms: seq[ShaderAttribute]= @[],
-  samplers: seq[ShaderAttribute]= @[],
-  outputs: seq[ShaderAttribute]= @[],
+  inputs: openArray[ShaderAttribute]=[],
+  uniforms: openArray[ShaderAttribute]=[],
+  samplers: openArray[ShaderAttribute]=[],
+  outputs: openArray[ShaderAttribute]=[],
   version=DEFAULT_SHADER_VERSION ,
   entrypoint=DEFAULT_SHADER_ENTRYPOINT ,
   main: string
@@ -143,83 +103,12 @@
     @[&"}}"]
   compileGlslToSPIRV(stage, code.join("\n"), entrypoint)
 
-proc compileVertexShader*(
-  inputs: seq[ShaderAttribute]= @[],
-  uniforms: seq[ShaderAttribute]= @[],
-  samplers: seq[ShaderAttribute]= @[],
-  outputs: seq[ShaderAttribute]= @[],
-  version=DEFAULT_SHADER_VERSION ,
-  entrypoint=DEFAULT_SHADER_ENTRYPOINT ,
-  main: string
-): ShaderCode {.compileTime.} =
-  compileGlslShader(
-    stage=VK_SHADER_STAGE_VERTEX_BIT,
-    inputs=inputs,
-    uniforms=uniforms,
-    samplers=samplers,
-    outputs=outputs,
-    version=version,
-    entrypoint=entrypoint,
-    main=main
-  )
-
-proc compileFragmentShader*(
-  inputs: seq[ShaderAttribute]= @[],
-  uniforms: seq[ShaderAttribute]= @[],
-  samplers: seq[ShaderAttribute]= @[],
-  outputs: seq[ShaderAttribute]= @[],
-  version=DEFAULT_SHADER_VERSION ,
-  entrypoint=DEFAULT_SHADER_ENTRYPOINT ,
-  main: string
-): ShaderCode {.compileTime.} =
-  compileGlslShader(
-    stage=VK_SHADER_STAGE_FRAGMENT_BIT,
-    inputs=inputs,
-    uniforms=uniforms,
-    samplers=samplers,
-    outputs=outputs,
-    version=version,
-    entrypoint=entrypoint,
-    main=main
-  )
-
-proc compileVertexFragmentShaderSet*(
-  inputs: seq[ShaderAttribute]= @[],
-  intermediate: seq[ShaderAttribute]= @[],
-  outputs: seq[ShaderAttribute]= @[],
-  uniforms: seq[ShaderAttribute]= @[],
-  samplers: seq[ShaderAttribute]= @[],
-  version=DEFAULT_SHADER_VERSION ,
-  entrypoint=DEFAULT_SHADER_ENTRYPOINT ,
-  vertexCode: string,
-  fragmentCode: string,
-): (ShaderCode, ShaderCode) {.compileTime.} =
-
-  result[0] = compileVertexShader(
-    inputs=inputs,
-    outputs=intermediate,
-    uniforms=uniforms,
-    samplers=samplers,
-    version=version,
-    entrypoint=entrypoint,
-    main=vertexCode
-  )
-  result[1] = compileFragmentShader(
-    inputs=intermediate,
-    outputs=outputs,
-    uniforms=uniforms,
-    samplers=samplers,
-    version=version,
-    entrypoint=entrypoint,
-    main=fragmentCode
-  )
-
 proc createShaderConfiguration*(
-  inputs: seq[ShaderAttribute]= @[],
-  intermediates: seq[ShaderAttribute]= @[],
-  outputs: seq[ShaderAttribute]= @[],
-  uniforms: seq[ShaderAttribute]= @[],
-  samplers: seq[ShaderAttribute]= @[],
+  inputs: openArray[ShaderAttribute]=[],
+  intermediates: openArray[ShaderAttribute]=[],
+  outputs: openArray[ShaderAttribute]=[],
+  uniforms: openArray[ShaderAttribute]=[],
+  samplers: openArray[ShaderAttribute]=[],
   version=DEFAULT_SHADER_VERSION ,
   entrypoint=DEFAULT_SHADER_ENTRYPOINT ,
   vertexCode: string,
@@ -236,45 +125,51 @@
     ),
     fragmentBinary: compileGlslCode(
       stage=VK_SHADER_STAGE_FRAGMENT_BIT,
-      inputs=inputs,
-      outputs=intermediates,
+      inputs=intermediates,
+      outputs=outputs,
       uniforms=uniforms,
       samplers=samplers,
       main=fragmentCode,
     ),
     entrypoint: entrypoint,
-    inputs: inputs,
-    intermediates: intermediates,
-    outputs: outputs,
-    uniforms: uniforms,
-    samplers: samplers,
+    inputs: @inputs,
+    intermediates: @intermediates,
+    outputs: @outputs,
+    uniforms: @uniforms,
+    samplers: @samplers,
   )
 
 
-proc createShaderModule*(
+proc createShaderModules*(
   device: Device,
-  shaderCode: ShaderCode,
-): Shader =
+  shaderConfiguration: ShaderConfiguration,
+): (ShaderModule, ShaderModule) =
   assert device.vk.valid
-  assert len(shaderCode.binary) > 0
+  assert len(shaderConfiguration.vertexBinary) > 0
+  assert len(shaderConfiguration.fragmentBinary) > 0
 
-  result.device = device
-  result.inputs = shaderCode.inputs
-  result.uniforms = shaderCode.uniforms
-  result.samplers = shaderCode.samplers
-  result.outputs = shaderCode.outputs
-  result.entrypoint = shaderCode.entrypoint
-  result.stage = shaderCode.stage
-  var bin = shaderCode.binary
-  var createInfo = VkShaderModuleCreateInfo(
+  result[0].device = device
+  result[1].device = device
+  result[0].configuration = shaderConfiguration
+  result[1].configuration = shaderConfiguration
+  result[0].stage = VK_SHADER_STAGE_VERTEX_BIT
+  result[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT
+
+  var createInfoVertex = VkShaderModuleCreateInfo(
     sType: VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
-    codeSize: uint(bin.len * sizeof(uint32)),
-    pCode: addr(bin[0]),
+    codeSize: uint(shaderConfiguration.vertexBinary.len * sizeof(uint32)),
+    pCode: addr(shaderConfiguration.vertexBinary[0]),
   )
-  checkVkResult vkCreateShaderModule(device.vk, addr(createInfo), nil, addr(result.vk))
+  checkVkResult vkCreateShaderModule(device.vk, addr(createInfoVertex), nil, addr(result[0].vk))
+  var createInfoFragment = VkShaderModuleCreateInfo(
+    sType: VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
+    codeSize: uint(shaderConfiguration.fragmentBinary.len * sizeof(uint32)),
+    pCode: addr(shaderConfiguration.fragmentBinary[0]),
+  )
+  checkVkResult vkCreateShaderModule(device.vk, addr(createInfoFragment), nil, addr(result[1].vk))
 
 proc getVertexInputInfo*(
-  shader: Shader,
+  shaderConfiguration: ShaderConfiguration,
   bindings: var seq[VkVertexInputBindingDescription],
   attributes: var seq[VkVertexInputAttributeDescription],
   baseBinding=0'u32
@@ -282,7 +177,7 @@
   var location = 0'u32
   var binding = baseBinding
 
-  for attribute in shader.inputs:
+  for attribute in shaderConfiguration.inputs:
     bindings.add VkVertexInputBindingDescription(
       binding: binding,
       stride: attribute.size,
@@ -308,15 +203,15 @@
   )
 
 
-proc getPipelineInfo*(shader: Shader): VkPipelineShaderStageCreateInfo =
+proc getPipelineInfo*(shader: ShaderModule): VkPipelineShaderStageCreateInfo =
   VkPipelineShaderStageCreateInfo(
     sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
     stage: shader.stage,
     module: shader.vk,
-    pName: cstring(shader.entrypoint),
+    pName: cstring(shader.configuration.entrypoint),
   )
 
-proc destroy*(shader: var Shader) =
+proc destroy*(shader: var ShaderModule) =
   assert shader.device.vk.valid
   assert shader.vk.valid
   shader.device.vk.vkDestroyShaderModule(shader.vk, nil)
--- a/tests/test_vulkan_wrapper.nim	Sun Aug 13 19:00:11 2023 +0700
+++ b/tests/test_vulkan_wrapper.nim	Tue Aug 15 23:51:37 2023 +0700
@@ -85,25 +85,20 @@
 
   # INIT RENDERER:
   const
-    vertexInput = @[
-      attr[Vec3f]("position", memoryPerformanceHint=PreferFastRead),
-      attr[Vec4f]("color", memoryPerformanceHint=PreferFastWrite),
-      attr[Vec3f]("translate", perInstance=true)
-    ]
-    intermediate = @[attr[Vec4f]("outcolor")]
-    fragOutput = @[attr[Vec4f]("color")]
-    samplers = @[attr[Sampler2DType]("my_little_texture")]
-    uniforms = @[attr[float32]("time")]
-    (vertexCode, fragmentCode) = compileVertexFragmentShaderSet(
-      inputs=vertexInput,
-      intermediate=intermediate,
-      outputs=fragOutput,
-      samplers=samplers,
-      uniforms=uniforms,
+    shaderConfiguration = createShaderConfiguration(
+      inputs=[
+        attr[Vec3f]("position", memoryPerformanceHint=PreferFastRead),
+        attr[Vec4f]("color", memoryPerformanceHint=PreferFastWrite),
+        attr[Vec3f]("translate", perInstance=true)
+      ],
+      intermediates=[attr[Vec4f]("outcolor")],
+      outputs=[attr[Vec4f]("color")],
+      uniforms=[attr[float32]("time")],
+      samplers=[attr[Sampler2DType]("my_little_texture")],
       vertexCode="""gl_Position = vec4(position + translate, 1.0); outcolor = color;""",
       fragmentCode="color = texture(my_little_texture, outcolor.xy) * 0.5 + outcolor * 0.5;",
     )
-  var renderPass = engine.gpuDevice.simpleForwardRenderPass(vertexCode, fragmentCode)
+  var renderPass = engine.gpuDevice.simpleForwardRenderPass(shaderConfiguration)
   engine.setRenderer(renderPass)
 
   # INIT SCENES