Mercurial > games > semicongine
changeset 111:6fd10b7e2d6a
did: allow runtime shader-input definitions
author | Sam <sam@basx.dev> |
---|---|
date | Fri, 31 Mar 2023 16:00:16 +0700 |
parents | 3bbc94a83404 |
children | 0c5a74885796 |
files | src/semicongine/gpu_data.nim src/semicongine/legacy/glsl.nim src/semicongine/scene.nim src/semicongine/vulkan/descriptor.nim src/semicongine/vulkan/glsl.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 | 10 files changed, 483 insertions(+), 294 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/semicongine/gpu_data.nim Fri Mar 31 16:00:16 2023 +0700 @@ -0,0 +1,186 @@ +import std/strformat +import std/tables + +import ./vulkan/api + +type + CountType = 1'u32 .. 4'u32 + DataType* = enum + Float32 + Float64 + Int8 + Int16 + Int32 + Int64 + UInt8 + UInt16 + UInt32 + UInt64 + Attribute* = object + name*: string + thetype*: DataType + components*: CountType # how many components the vectors has (1 means scalar) + rows*: CountType # used to split matrices into rows of vectors + perInstance*: bool + AttributeGroup* = object + attributes*: seq[Attribute] + +func attr*(name: string, thetype: DataType, components=CountType(1), rows=CountType(1), perInstance=false): auto = + Attribute(name: name, thetype: thetype, components: components, rows: rows, perInstance: perInstance) + +func size*(thetype: DataType): uint32 = + case thetype: + of Float32: 4 + of Float64: 8 + of Int8: 1 + of Int16: 2 + of Int32: 4 + of Int64: 8 + of UInt8: 1 + of UInt16: 2 + of UInt32: 4 + of UInt64: 8 + +func size*(attribute: Attribute, perRow=false): uint32 = + if perRow: + attribute.thetype.size * attribute.components + else: + attribute.thetype.size * attribute.components * attribute.rows + +func size*(thetype: AttributeGroup): uint32 = + for attribute in thetype.attributes: + result += attribute.size + +const TYPEMAP = { + CountType(1): { + UInt8: VK_FORMAT_R8_UINT, + Int8: VK_FORMAT_R8_SINT, + UInt16: VK_FORMAT_R16_UINT, + Int16: VK_FORMAT_R16_SINT, + UInt32: VK_FORMAT_R32_UINT, + Int32: VK_FORMAT_R32_SINT, + UInt64: VK_FORMAT_R64_UINT, + Int64: VK_FORMAT_R64_SINT, + Float32: VK_FORMAT_R32_SFLOAT, + Float64: VK_FORMAT_R64_SFLOAT, + }.toTable, + CountType(2): { + UInt8: VK_FORMAT_R8G8_UINT, + Int8: VK_FORMAT_R8G8_SINT, + UInt16: VK_FORMAT_R16G16_UINT, + Int16: VK_FORMAT_R16G16_SINT, + UInt32: VK_FORMAT_R32G32_UINT, + Int32: VK_FORMAT_R32G32_SINT, + UInt64: VK_FORMAT_R64G64_UINT, + Int64: VK_FORMAT_R64G64_SINT, + Float32: VK_FORMAT_R32G32_SFLOAT, + Float64: VK_FORMAT_R64G64_SFLOAT, + }.toTable, + CountType(3): { + UInt8: VK_FORMAT_R8G8B8_UINT, + Int8: VK_FORMAT_R8G8B8_SINT, + UInt16: VK_FORMAT_R16G16B16_UINT, + Int16: VK_FORMAT_R16G16B16_SINT, + UInt32: VK_FORMAT_R32G32B32_UINT, + Int32: VK_FORMAT_R32G32B32_SINT, + UInt64: VK_FORMAT_R64G64B64_UINT, + Int64: VK_FORMAT_R64G64B64_SINT, + Float32: VK_FORMAT_R32G32B32_SFLOAT, + Float64: VK_FORMAT_R64G64B64_SFLOAT, + }.toTable, + CountType(4): { + UInt8: VK_FORMAT_R8G8B8A8_UINT, + Int8: VK_FORMAT_R8G8B8A8_SINT, + UInt16: VK_FORMAT_R16G16B16A16_UINT, + Int16: VK_FORMAT_R16G16B16A16_SINT, + UInt32: VK_FORMAT_R32G32B32A32_UINT, + Int32: VK_FORMAT_R32G32B32A32_SINT, + UInt64: VK_FORMAT_R64G64B64A64_UINT, + Int64: VK_FORMAT_R64G64B64A64_SINT, + Float32: VK_FORMAT_R32G32B32A32_SFLOAT, + Float64: VK_FORMAT_R64G64B64A64_SFLOAT, + }.toTable, +}.toTable + +func getVkFormat*(value: Attribute): VkFormat = + TYPEMAP[value.components][value.thetype] + +# from https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap15.html +func nLocationSlots*(attribute: Attribute): uint32 = + #[ + single location: + 16-bit scalar and vector types, and + 32-bit scalar and vector types, and + 64-bit scalar and 2-component vector types. + two locations + 64-bit three- and four-component vectors + ]# + case attribute.thetype: + of Float32: 1 + of Float64: (if attribute.components < 3: 1 else: 2) + of Int8: 1 + of Int16: 1 + of Int32: 1 + of Int64: (if attribute.components < 3: 1 else: 2) + of UInt8: 1 + of UInt16: 1 + of UInt32: 1 + of UInt64: (if attribute.components < 3: 1 else: 2) + +func glslType*(attribute: Attribute): string = + # todo: likely not correct as we would need to enable some + # extensions somewhere (Vulkan/GLSL compiler?) to have + # everything work as intended. Or maybe the GPU driver does + # some automagic conversion stuf.. + + # used to ensure square matrix get only one number for side instead of two, e.g. mat2 instead of mat22 + let matrixColumns = if attribute.components == attribute.rows: "" else: $attribute.components + case attribute.rows: + of 1: + case attribute.components: + of 1: # scalars + case attribute.thetype: + of Float32: "float" + of Float64: "double" + of Int8, Int16, Int32, Int64: "int" + of UInt8, UInt16, UInt32, UInt64: "uint" + else: # vectors + case attribute.thetype: + of Float32: &"vec{attribute.components}" + of Float64: &"dvec{attribute.components}" + of Int8, Int16, Int32, Int64: &"ivec{attribute.components}" + of UInt8, UInt16, UInt32, UInt64: &"uvec{attribute.components}" + else: + case attribute.components: + of 1: raise newException(Exception, &"Unsupported matrix-column-count: {attribute.components}") + else: + case attribute.thetype: + of Float32: &"mat{attribute.rows}{matrixColumns}" + of Float64: &"dmat{attribute.rows}{matrixColumns}" + else: raise newException(Exception, &"Unsupported matrix-component type: {attribute.thetype}") + +func glslInput*(group: AttributeGroup): seq[string] = + if group.attributes.len == 0: + return @[] + var i = 0'u32 + for attribute in group.attributes: + result.add &"layout(location = {i}) in {attribute.glslType} {attribute.name};" + for j in 0 ..< attribute.rows: + i += attribute.nLocationSlots + +func glslUniforms*(group: AttributeGroup, blockName="Uniforms", binding=0): seq[string] = + if group.attributes.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: + result.add(&" {attribute.glslType} {attribute.name};") + result.add(&"}} {blockName};") + +func glslOutput*(group: AttributeGroup): seq[string] = + if group.attributes.len == 0: + return @[] + var i = 0'u32 + for attribute in group.attributes: + result.add &"layout(location = {i}) out {attribute.glslType} {attribute.name};" + i += 1
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/semicongine/legacy/glsl.nim Fri Mar 31 16:00:16 2023 +0700 @@ -0,0 +1,132 @@ +import std/typetraits +import std/strformat +import ../math/vector +import ../math/matrix + + +func getGLSLType*[T](t: T): string {.compileTime.} = + # todo: likely not correct as we would need to enable some + # extensions somewhere (Vulkan/GLSL compiler?) to have + # everything work as intended. Or maybe the GPU driver does + # some automagic conversion stuf.. + when T is uint8: "uint" + elif T is int8: "int" + elif T is uint16: "uint" + elif T is int16: "int" + elif T is uint32: "uint" + elif T is int32: "int" + elif T is uint64: "uint" + elif T is int64: "int" + elif T is float32: "float" + elif T is float64: "double" + + elif T is TVec2[uint8]: "uvec2" + elif T is TVec2[int8]: "ivec2" + elif T is TVec2[uint16]: "uvec2" + elif T is TVec2[int16]: "ivec2" + elif T is TVec2[uint32]: "uvec2" + elif T is TVec2[int32]: "ivec2" + elif T is TVec2[uint64]: "uvec2" + elif T is TVec2[int64]: "ivec2" + elif T is TVec2[float32]: "vec2" + elif T is TVec2[float64]: "dvec2" + + elif T is TVec3[uint8]: "uvec3" + elif T is TVec3[int8]: "ivec3" + elif T is TVec3[uint16]: "uvec3" + elif T is TVec3[int16]: "ivec3" + elif T is TVec3[uint32]: "uvec3" + elif T is TVec3[int32]: "ivec3" + elif T is TVec3[uint64]: "uvec3" + elif T is TVec3[int64]: "ivec3" + elif T is TVec3[float32]: "vec3" + elif T is TVec3[float64]: "dvec3" + + elif T is TVec4[uint8]: "uvec4" + elif T is TVec4[int8]: "ivec4" + elif T is TVec4[uint16]: "uvec4" + elif T is TVec4[int16]: "ivec4" + elif T is TVec4[uint32]: "uvec4" + elif T is TVec4[int32]: "ivec4" + elif T is TVec4[uint64]: "uvec4" + elif T is TVec4[int64]: "ivec4" + elif T is TVec4[float32]: "vec4" + elif T is TVec4[float64]: "dvec4" + + elif T is TMat22[float32]: "mat2" + elif T is TMat23[float32]: "mat32" + elif T is TMat32[float32]: "mat23" + elif T is TMat33[float32]: "mat3" + elif T is TMat34[float32]: "mat43" + elif T is TMat43[float32]: "mat34" + elif T is TMat44[float32]: "mat4" + + elif T is TMat22[float64]: "dmat2" + elif T is TMat23[float64]: "dmat32" + elif T is TMat32[float64]: "dmat23" + elif T is TMat33[float64]: "dmat3" + elif T is TMat34[float64]: "dmat43" + elif T is TMat43[float64]: "dmat34" + elif T is TMat44[float64]: "dmat4" + + +# return the number of elements into which larger types are divided +func compositeAttributesNumber*[T](value: T): int = + when T is TMat33[float32]: + 3 + elif T is TMat44[float32]: + 4 + else: + 1 + + +# from https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap15.html +func nLocationSlots*[T](value: T): uint32 = + when (T is TVec3[float64] or T is TVec3[uint64] or T is TVec4[float64] or T is TVec4[float64]): + return 2 + elif T is SomeNumber or T is TVec: + return 1 + else: + raise newException(Exception, "Unsupported vertex attribute type") + + +# return the type into which larger types are divided +func compositeAttribute*[T](value: T): auto = + when T is TMat33[float32]: + Vec3() + elif T is TMat44[float32]: + Vec4() + else: + value + +func glslInput*[T](): seq[string] {.compileTime.} = + when not (T is void): + var i = 0'u32 + for fieldname, value in default(T).fieldPairs: + let glsltype = getGLSLType(value) + let thename = fieldname + result.add &"layout(location = {i}) in {glsltype} {thename};" + for j in 0 ..< compositeAttributesNumber(value): + i += nLocationSlots(compositeAttribute(value)) + +func glslUniforms*[T](): seq[string] {.compileTime.} = + # currently only a single uniform block supported, therefore binding = 0 + when not (T is void): + let uniformName = name(T) + result.add(&"layout(binding = 0) uniform T{uniformName} {{") + for fieldname, value in default(T).fieldPairs: + let glsltype = getGLSLType(value) + let thename = fieldname + result.add(&" {glsltype} {thename};") + result.add(&"}} {uniformName};") + +func glslOutput*[T](): seq[string] {.compileTime.} = + when not (T is void): + var i = 0'u32 + for fieldname, value in default(T).fieldPairs: + let glsltype = getGLSLType(value) + let thename = fieldname + result.add &"layout(location = {i}) out {glsltype} {thename};" + i += 1 + else: + result
--- a/src/semicongine/scene.nim Thu Mar 30 00:00:54 2023 +0700 +++ b/src/semicongine/scene.nim Fri Mar 31 16:00:16 2023 +0700 @@ -1,13 +1,15 @@ -import std/options +import std/tables import ./vulkan/api import ./entity import ./vulkan/buffer +import ./vulkan/pipeline +import ./vulkan/renderpass type Drawable* = object - buffers*: seq[Buffer] - elementCount*: uint32 + buffers*: seq[(Buffer, int)] # buffer + offset from buffer + elementCount*: uint32 # vertices or indices instanceCount*: uint32 case indexed*: bool of true: @@ -18,8 +20,20 @@ Scene* = object root*: Entity - drawables: seq[Drawable] + drawables: Table[VkPipeline, seq[Drawable]] + +proc setupDrawables(scene: var Scene, pipeline: Pipeline) = + # echo pipeline.descriptorSetLayout.descriptors + # thetype*: VkDescriptorType + # count*: uint32 + # itemsize*: uint32 + scene.drawables[pipeline.vk] = @[] -proc getDrawables*(scene: Scene): seq[Drawable] = - # TODO: create and fill buffers - result +proc setupDrawables*(scene: var Scene, renderPass: var RenderPass) = + for subpass in renderPass.subpasses.mitems: + for pipeline in subpass.pipelines.mitems: + scene.setupDrawables(pipeline) + + +proc getDrawables*(scene: Scene, pipeline: Pipeline): seq[Drawable] = + scene.drawables.getOrDefault(pipeline.vk, @[])
--- a/src/semicongine/vulkan/descriptor.nim Thu Mar 30 00:00:54 2023 +0700 +++ b/src/semicongine/vulkan/descriptor.nim Fri Mar 31 16:00:16 2023 +0700 @@ -14,7 +14,7 @@ DescriptorSetLayout* = object # "type description of a DescriptorSet device: Device vk*: VkDescriptorSetLayout - descriptors: seq[Descriptor] + descriptors*: seq[Descriptor] DescriptorSet* = object # "instance" of a DescriptorSetLayout vk*: VkDescriptorSet layout: DescriptorSetLayout
--- a/src/semicongine/vulkan/glsl.nim Thu Mar 30 00:00:54 2023 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,132 +0,0 @@ -import std/typetraits -import std/strformat -import ../math/vector -import ../math/matrix - - -func getGLSLType*[T](t: T): string {.compileTime.} = - # todo: likely not correct as we would need to enable some - # extensions somewhere (Vulkan/GLSL compiler?) to have - # everything work as intended. Or maybe the GPU driver does - # some automagic conversion stuf.. - when T is uint8: "uint" - elif T is int8: "int" - elif T is uint16: "uint" - elif T is int16: "int" - elif T is uint32: "uint" - elif T is int32: "int" - elif T is uint64: "uint" - elif T is int64: "int" - elif T is float32: "float" - elif T is float64: "double" - - elif T is TVec2[uint8]: "uvec2" - elif T is TVec2[int8]: "ivec2" - elif T is TVec2[uint16]: "uvec2" - elif T is TVec2[int16]: "ivec2" - elif T is TVec2[uint32]: "uvec2" - elif T is TVec2[int32]: "ivec2" - elif T is TVec2[uint64]: "uvec2" - elif T is TVec2[int64]: "ivec2" - elif T is TVec2[float32]: "vec2" - elif T is TVec2[float64]: "dvec2" - - elif T is TVec3[uint8]: "uvec3" - elif T is TVec3[int8]: "ivec3" - elif T is TVec3[uint16]: "uvec3" - elif T is TVec3[int16]: "ivec3" - elif T is TVec3[uint32]: "uvec3" - elif T is TVec3[int32]: "ivec3" - elif T is TVec3[uint64]: "uvec3" - elif T is TVec3[int64]: "ivec3" - elif T is TVec3[float32]: "vec3" - elif T is TVec3[float64]: "dvec3" - - elif T is TVec4[uint8]: "uvec4" - elif T is TVec4[int8]: "ivec4" - elif T is TVec4[uint16]: "uvec4" - elif T is TVec4[int16]: "ivec4" - elif T is TVec4[uint32]: "uvec4" - elif T is TVec4[int32]: "ivec4" - elif T is TVec4[uint64]: "uvec4" - elif T is TVec4[int64]: "ivec4" - elif T is TVec4[float32]: "vec4" - elif T is TVec4[float64]: "dvec4" - - elif T is TMat22[float32]: "mat2" - elif T is TMat23[float32]: "mat32" - elif T is TMat32[float32]: "mat23" - elif T is TMat33[float32]: "mat3" - elif T is TMat34[float32]: "mat43" - elif T is TMat43[float32]: "mat34" - elif T is TMat44[float32]: "mat4" - - elif T is TMat22[float64]: "dmat2" - elif T is TMat23[float64]: "dmat32" - elif T is TMat32[float64]: "dmat23" - elif T is TMat33[float64]: "dmat3" - elif T is TMat34[float64]: "dmat43" - elif T is TMat43[float64]: "dmat34" - elif T is TMat44[float64]: "dmat4" - - -# return the number of elements into which larger types are divided -func compositeAttributesNumber*[T](value: T): int = - when T is TMat33[float32]: - 3 - elif T is TMat44[float32]: - 4 - else: - 1 - - -# from https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap15.html -func nLocationSlots*[T](value: T): uint32 = - when (T is TVec3[float64] or T is TVec3[uint64] or T is TVec4[float64] or T is TVec4[float64]): - return 2 - elif T is SomeNumber or T is TVec: - return 1 - else: - raise newException(Exception, "Unsupported vertex attribute type") - - -# return the type into which larger types are divided -func compositeAttribute*[T](value: T): auto = - when T is TMat33[float32]: - Vec3() - elif T is TMat44[float32]: - Vec4() - else: - value - -func glslInput*[T](): seq[string] {.compileTime.} = - when not (T is void): - var i = 0'u32 - for fieldname, value in default(T).fieldPairs: - let glsltype = getGLSLType(value) - let thename = fieldname - result.add &"layout(location = {i}) in {glsltype} {thename};" - for j in 0 ..< compositeAttributesNumber(value): - i += nLocationSlots(compositeAttribute(value)) - -func glslUniforms*[T](): seq[string] {.compileTime.} = - # currently only a single uniform block supported, therefore binding = 0 - when not (T is void): - let uniformName = name(T) - result.add(&"layout(binding = 0) uniform T{uniformName} {{") - for fieldname, value in default(T).fieldPairs: - let glsltype = getGLSLType(value) - let thename = fieldname - result.add(&" {glsltype} {thename};") - result.add(&"}} {uniformName};") - -func glslOutput*[T](): seq[string] {.compileTime.} = - when not (T is void): - var i = 0'u32 - for fieldname, value in default(T).fieldPairs: - let glsltype = getGLSLType(value) - let thename = fieldname - result.add &"layout(location = {i}) out {glsltype} {thename};" - i += 1 - else: - result
--- a/src/semicongine/vulkan/pipeline.nim Thu Mar 30 00:00:54 2023 +0700 +++ b/src/semicongine/vulkan/pipeline.nim Fri Mar 31 16:00:16 2023 +0700 @@ -1,12 +1,7 @@ -import std/options - import ./api -import ./utils import ./device import ./descriptor -import ../scene - type Pipeline* = object device*: Device @@ -16,38 +11,6 @@ descriptorPool*: DescriptorPool descriptorSets*: seq[DescriptorSet] -proc run*(pipeline: Pipeline, commandBuffer: VkCommandBuffer, inFlightFrame: int, scene: Scene) = - var varPipeline = pipeline - commandBuffer.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.vk) - commandBuffer.vkCmdBindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.layout, 0, 1, addr(varPipeline.descriptorSets[inFlightFrame].vk), 0, nil) - for drawable in scene.getDrawables(): - var buffers: seq[VkBuffer] - var offsets: seq[VkDeviceSize] - for buffer in drawable.buffers: - buffers.add buffer.vk - offsets.add VkDeviceSize(0) - commandBuffer.vkCmdBindVertexBuffers( - firstBinding=0'u32, - bindingCount=uint32(buffers.len), - pBuffers=buffers.toCPointer(), - pOffsets=offsets.toCPointer() - ) - if drawable.indexed: - commandBuffer.vkCmdBindIndexBuffer(drawable.indexBuffer.vk, VkDeviceSize(0), 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 destroy*(pipeline: var Pipeline) = assert pipeline.device.vk.valid
--- a/src/semicongine/vulkan/renderpass.nim Thu Mar 30 00:00:54 2023 +0700 +++ b/src/semicongine/vulkan/renderpass.nim Fri Mar 31 16:00:16 2023 +0700 @@ -6,13 +6,15 @@ import ./pipeline import ./shader import ./descriptor +import ../gpu_data + import ../math type Subpass* = object clearColor*: Vec4 + pipelineBindPoint*: VkPipelineBindPoint flags: VkSubpassDescriptionFlags - pipelineBindPoint: VkPipelineBindPoint inputs: seq[VkAttachmentReference] outputs: seq[VkAttachmentReference] resolvers: seq[VkAttachmentReference] @@ -66,7 +68,7 @@ result.subpasses = pSubpasses checkVkResult device.vk.vkCreateRenderPass(addr(createInfo), nil, addr(result.vk)) -proc attachPipeline[VertexShader: Shader, FragmentShader: Shader](renderPass: var RenderPass, vertexShader: VertexShader, fragmentShader: FragmentShader, subpass = 0'u32) = +proc attachPipeline(renderPass: var RenderPass, vertexShader: Shader, fragmentShader: Shader, subpass = 0'u32) = assert renderPass.vk.valid assert renderPass.device.vk.valid assert vertexShader.stage == VK_SHADER_STAGE_VERTEX_BIT @@ -78,16 +80,16 @@ thetype: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, count: 1, stages: @[VK_SHADER_STAGE_VERTEX_BIT], - itemsize: uint32(sizeof(shaderUniforms(vertexShader))) + itemsize: vertexShader.uniforms.size(), )] - when shaderUniforms(vertexShader) is shaderUniforms(fragmentShader): + if vertexShader.uniforms == fragmentShader.uniforms: descriptors[0].stages.add VK_SHADER_STAGE_FRAGMENT_BIT else: descriptors.add Descriptor( thetype: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, count: 1, stages: @[VK_SHADER_STAGE_FRAGMENT_BIT], - itemsize: uint32(sizeof(shaderUniforms(fragmentShader))) + itemsize: fragmentShader.uniforms.size(), ) pipeline.descriptorSetLayout = renderPass.device.createDescriptorSetLayout(descriptors) @@ -197,7 +199,7 @@ pipeline.descriptorSets = pipeline.descriptorPool.allocateDescriptorSet(pipeline.descriptorSetLayout, renderPass.inFlightFrames) renderPass.subpasses[subpass].pipelines.add pipeline -proc simpleForwardRenderPass*[VertexShader: Shader, FragmentShader: Shader](device: Device, format: VkFormat, vertexShader: VertexShader, fragmentShader: FragmentShader, inFlightFrames: int, clearColor=Vec4([0.5'f32, 0.5'f32, 0.5'f32, 1'f32])): RenderPass = +proc simpleForwardRenderPass*(device: Device, format: VkFormat, vertexShader: Shader, fragmentShader: Shader, inFlightFrames: int, clearColor=Vec4([0.8'f32, 0.8'f32, 0.8'f32, 1'f32])): RenderPass = assert device.vk.valid var attachments = @[VkAttachmentDescription( @@ -229,12 +231,12 @@ result = device.createRenderPass(attachments=attachments, subpasses=subpasses, dependencies=dependencies, inFlightFrames=inFlightFrames) result.attachPipeline(vertexShader, fragmentShader, 0) -proc destroy*(renderpass: var RenderPass) = - assert renderpass.device.vk.valid - assert renderpass.vk.valid - renderpass.device.vk.vkDestroyRenderPass(renderpass.vk, nil) - renderpass.vk.reset - for subpass in renderpass.subpasses.mitems: +proc destroy*(renderPass: var RenderPass) = + assert renderPass.device.vk.valid + assert renderPass.vk.valid + renderPass.device.vk.vkDestroyRenderPass(renderPass.vk, nil) + renderPass.vk.reset + for subpass in renderPass.subpasses.mitems: for pipeline in subpass.pipelines.mitems: pipeline.destroy() - renderpass.subpasses = @[] + renderPass.subpasses = @[]
--- a/src/semicongine/vulkan/shader.nim Thu Mar 30 00:00:54 2023 +0700 +++ b/src/semicongine/vulkan/shader.nim Fri Mar 31 16:00:16 2023 +0700 @@ -12,22 +12,22 @@ import ./api import ./device import ./vertex -import ./glsl import ./utils +import ../gpu_data + let logger = newConsoleLogger() addHandler(logger) type - Shader*[Inputs, Uniforms, Outputs] = object + Shader* = object device: Device stage*: VkShaderStageFlagBits vk*: VkShaderModule entrypoint*: string - -template shaderInput*[Inputs, Uniforms, Outputs](shader: Shader[Inputs, Uniforms, Outputs]): typedesc = Inputs -template shaderOutputs*[Inputs, Uniforms, Outputs](shader: Shader[Inputs, Uniforms, Outputs]): typedesc = Outputs -template shaderUniforms*[Inputs, Uniforms, Outputs](shader: Shader[Inputs, Uniforms, Outputs]): typedesc = Uniforms + inputs*: AttributeGroup + uniforms*: AttributeGroup + outputs*: AttributeGroup proc compileGLSLToSPIRV*(stage: VkShaderStageFlagBits, shaderSource: string, entrypoint: string): seq[uint32] {.compileTime.} = @@ -78,26 +78,53 @@ i += 4 -proc shaderCode*[Inputs, Uniforms, Outputs](stage: VkShaderStageFlagBits, version: int, entrypoint: string, body: seq[string]): seq[uint32] {.compileTime.} = +proc shaderCode*( + inputs: AttributeGroup, + uniforms: AttributeGroup, + outputs: AttributeGroup, + stage: VkShaderStageFlagBits, + version: int, + entrypoint: string, + body: seq[string] +): seq[uint32] {.compileTime.} = var code = @[&"#version {version}", ""] & - glslInput[Inputs]() & @[""] & - glslUniforms[Uniforms]() & @[""] & - glslOutput[Outputs]() & @[""] & + inputs.glslInput() & @[""] & + uniforms.glslUniforms() & @[""] & + outputs.glslOutput() & @[""] & @[&"void {entrypoint}(){{"] & body & @[&"}}"] compileGLSLToSPIRV(stage, code.join("\n"), entrypoint) -proc shaderCode*[Inputs, Uniforms, Outputs](stage: VkShaderStageFlagBits, version: int, entrypoint: string, body: string): seq[uint32] {.compileTime.} = - return shaderCode[Inputs, Uniforms, Outputs](stage, version, entrypoint, @[body]) +proc shaderCode*( + inputs: AttributeGroup, + uniforms: AttributeGroup, + outputs: AttributeGroup, + stage: VkShaderStageFlagBits, + version: int, + entrypoint: string, + body: string +): seq[uint32] {.compileTime.} = + return shaderCode(inputs, uniforms, outputs, stage, version, entrypoint, @[body]) -proc createShader*[Inputs, Uniforms, Outputs](device: Device, stage: VkShaderStageFlagBits, entrypoint: string, binary: seq[uint32]): Shader[Inputs, Uniforms, Outputs] = +proc createShader*( + device: Device, + inputs: AttributeGroup, + uniforms: AttributeGroup, + outputs: AttributeGroup, + stage: VkShaderStageFlagBits, + entrypoint: string, + binary: seq[uint32] +): Shader = assert device.vk.valid assert len(binary) > 0 result.device = device + result.inputs = inputs + result.uniforms = uniforms + result.outputs = outputs result.entrypoint = entrypoint result.stage = stage var bin = binary @@ -108,6 +135,41 @@ ) checkVkResult vkCreateShaderModule(device.vk, addr(createInfo), nil, addr(result.vk)) +proc getVertexInputInfo*( + shader: Shader, + bindings: var seq[VkVertexInputBindingDescription], + attributes: var seq[VkVertexInputAttributeDescription], + baseBinding=0'u32 +): VkPipelineVertexInputStateCreateInfo = + var location = 0'u32 + var binding = baseBinding + + for attribute in shader.inputs.attributes: + bindings.add VkVertexInputBindingDescription( + binding: binding, + stride: attribute.size, + inputRate: if attribute.perInstance: VK_VERTEX_INPUT_RATE_INSTANCE else: VK_VERTEX_INPUT_RATE_VERTEX, + ) + # allows to submit larger data structures like Mat44, for most other types will be 1 + for i in 0 ..< attribute.rows: + attributes.add VkVertexInputAttributeDescription( + binding: binding, + location: location, + format: getVkFormat(attribute), + offset: i * attribute.size(perRow=true), + ) + location += attribute.nLocationSlots + inc binding + + return VkPipelineVertexInputStateCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + vertexBindingDescriptionCount: uint32(bindings.len), + pVertexBindingDescriptions: bindings.toCPointer, + vertexAttributeDescriptionCount: uint32(attributes.len), + pVertexAttributeDescriptions: attributes.toCPointer, + ) + + proc getPipelineInfo*(shader: Shader): VkPipelineShaderStageCreateInfo = VkPipelineShaderStageCreateInfo( sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, @@ -121,81 +183,3 @@ assert shader.vk.valid shader.device.vk.vkDestroyShaderModule(shader.vk, nil) shader.vk.reset - - -func getVkFormat[T](value: T): VkFormat = - when T is uint8: VK_FORMAT_R8_UINT - elif T is int8: VK_FORMAT_R8_SINT - elif T is uint16: VK_FORMAT_R16_UINT - elif T is int16: VK_FORMAT_R16_SINT - elif T is uint32: VK_FORMAT_R32_UINT - elif T is int32: VK_FORMAT_R32_SINT - elif T is uint64: VK_FORMAT_R64_UINT - elif T is int64: VK_FORMAT_R64_SINT - elif T is float32: VK_FORMAT_R32_SFLOAT - elif T is float64: VK_FORMAT_R64_SFLOAT - elif T is TVec2[uint8]: VK_FORMAT_R8G8_UINT - elif T is TVec2[int8]: VK_FORMAT_R8G8_SINT - elif T is TVec2[uint16]: VK_FORMAT_R16G16_UINT - elif T is TVec2[int16]: VK_FORMAT_R16G16_SINT - elif T is TVec2[uint32]: VK_FORMAT_R32G32_UINT - elif T is TVec2[int32]: VK_FORMAT_R32G32_SINT - elif T is TVec2[uint64]: VK_FORMAT_R64G64_UINT - elif T is TVec2[int64]: VK_FORMAT_R64G64_SINT - elif T is TVec2[float32]: VK_FORMAT_R32G32_SFLOAT - elif T is TVec2[float64]: VK_FORMAT_R64G64_SFLOAT - elif T is TVec3[uint8]: VK_FORMAT_R8G8B8_UINT - elif T is TVec3[int8]: VK_FORMAT_R8G8B8_SINT - elif T is TVec3[uint16]: VK_FORMAT_R16G16B16_UINT - elif T is TVec3[int16]: VK_FORMAT_R16G16B16_SINT - elif T is TVec3[uint32]: VK_FORMAT_R32G32B32_UINT - elif T is TVec3[int32]: VK_FORMAT_R32G32B32_SINT - elif T is TVec3[uint64]: VK_FORMAT_R64G64B64_UINT - elif T is TVec3[int64]: VK_FORMAT_R64G64B64_SINT - elif T is TVec3[float32]: VK_FORMAT_R32G32B32_SFLOAT - elif T is TVec3[float64]: VK_FORMAT_R64G64B64_SFLOAT - elif T is TVec4[uint8]: VK_FORMAT_R8G8B8A8_UINT - elif T is TVec4[int8]: VK_FORMAT_R8G8B8A8_SINT - elif T is TVec4[uint16]: VK_FORMAT_R16G16B16A16_UINT - elif T is TVec4[int16]: VK_FORMAT_R16G16B16A16_SINT - elif T is TVec4[uint32]: VK_FORMAT_R32G32B32A32_UINT - elif T is TVec4[int32]: VK_FORMAT_R32G32B32A32_SINT - elif T is TVec4[uint64]: VK_FORMAT_R64G64B64A64_UINT - elif T is TVec4[int64]: VK_FORMAT_R64G64B64A64_SINT - elif T is TVec4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT - elif T is TVec4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT - else: {.error: "Unsupported vertex attribute type".} - - -proc getVertexInputInfo*[Input, Uniforms, Output]( - shader: Shader[Input, Uniforms, Output], - bindings: var seq[VkVertexInputBindingDescription], - attributes: var seq[VkVertexInputAttributeDescription], -): VkPipelineVertexInputStateCreateInfo = - var location = 0'u32 - var binding = 0'u32 - - for name, value in default(Input).fieldPairs: - bindings.add VkVertexInputBindingDescription( - binding: binding, - stride: uint32(sizeof(value)), - inputRate: if value.hasCustomPragma(PerInstance): VK_VERTEX_INPUT_RATE_INSTANCE else: VK_VERTEX_INPUT_RATE_VERTEX, - ) - # allows to submit larger data structures like Mat44, for most other types will be 1 - for i in 0 ..< compositeAttributesNumber(value): - attributes.add VkVertexInputAttributeDescription( - binding: binding, - location: location, - format: getVkFormat(compositeAttribute(value)), - offset: uint32(i * sizeof(compositeAttribute(value))), - ) - location += nLocationSlots(compositeAttribute(value)) - inc binding - - return VkPipelineVertexInputStateCreateInfo( - sType: VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, - vertexBindingDescriptionCount: uint32(bindings.len), - pVertexBindingDescriptions: bindings.toCPointer, - vertexAttributeDescriptionCount: uint32(attributes.len), - pVertexAttributeDescriptions: attributes.toCPointer, - )
--- a/src/semicongine/vulkan/swapchain.nim Thu Mar 30 00:00:54 2023 +0700 +++ b/src/semicongine/vulkan/swapchain.nim Fri Mar 31 16:00:16 2023 +0700 @@ -100,7 +100,37 @@ return (swapchain, createResult) -proc drawNextFrame*(swapchain: var Swapchain, scene: Scene): bool = +proc draw*(commandBuffer: VkCommandBuffer, drawables: seq[Drawable], scene: Scene) = + for drawable in drawables: + var buffers: seq[VkBuffer] + var offsets: seq[VkDeviceSize] + for (buffer, offset) in drawable.buffers: + buffers.add buffer.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(drawable.indexBuffer.vk, VkDeviceSize(0), 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 assert swapchain.vk.valid assert swapchain.device.firstGraphicsQueue().isSome @@ -131,8 +161,10 @@ swapchain.framebuffers[currentFramebufferIndex] ): for i in 0 ..< swapchain.renderpass.subpasses.len: - for pipeline in swapchain.renderpass.subpasses[i].pipelines: - pipeline.run(commandBuffer, swapchain.currentInFlight, scene) + 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) + commandBuffer.draw(scene.getDrawables(pipeline), scene) if i < swapchain.renderpass.subpasses.len - 1: commandBuffer.vkCmdNextSubpass(VK_SUBPASS_CONTENTS_INLINE) @@ -194,3 +226,4 @@ swapchain.renderFinishedSemaphore[i].destroy() swapchain.device.vk.vkDestroySwapchainKHR(swapchain.vk, nil) swapchain.vk.reset() +
--- a/tests/test_vulkan_wrapper.nim Thu Mar 30 00:00:54 2023 +0700 +++ b/tests/test_vulkan_wrapper.nim Fri Mar 31 16:00:16 2023 +0700 @@ -1,3 +1,4 @@ +import std/os import std/options import semicongine/vulkan @@ -5,6 +6,7 @@ import semicongine/math import semicongine/entity import semicongine/scene +import semicongine/gpu_data type Vertex = object @@ -66,22 +68,27 @@ selectedPhysicalDevice.filterForGraphicsPresentationQueues() ) - const vertexBinary = shaderCode[Vertex, Uniforms, FragmentInput](stage=VK_SHADER_STAGE_VERTEX_BIT, version=450, entrypoint="main", "fragpos = pos;") - const fragmentBinary = shaderCode[FragmentInput, void, Pixel](stage=VK_SHADER_STAGE_FRAGMENT_BIT, version=450, entrypoint="main", "color = vec4(1, 1, 1, 0);") + const inputs = AttributeGroup(attributes: @[attr(name="pos", thetype=Float32, components=3)]) + const uniforms = AttributeGroup() + const outputs = AttributeGroup(attributes: @[attr(name="fragpos", thetype=Float32, components=3)]) + const fragOutput = AttributeGroup(attributes: @[attr(name="color", thetype=Float32, components=4)]) + const vertexBinary = shaderCode(inputs=inputs, uniforms=uniforms, outputs=outputs, stage=VK_SHADER_STAGE_VERTEX_BIT, version=450, entrypoint="main", "fragpos = pos;") + const fragmentBinary = shaderCode(inputs=outputs, uniforms=uniforms, outputs=fragOutput, stage=VK_SHADER_STAGE_FRAGMENT_BIT, version=450, entrypoint="main", "color = vec4(1, 1, 1, 0);") var - vertexshader = createShader[Vertex, Uniforms, FragmentInput](device, VK_SHADER_STAGE_VERTEX_BIT, "main", vertexBinary) - fragmentshader = createShader[FragmentInput, void, Pixel](device, VK_SHADER_STAGE_FRAGMENT_BIT, "main", fragmentBinary) + vertexshader = device.createShader(inputs, uniforms, outputs, VK_SHADER_STAGE_VERTEX_BIT, "main", vertexBinary) + fragmentshader = device.createShader(inputs, uniforms, outputs, VK_SHADER_STAGE_FRAGMENT_BIT, "main", fragmentBinary) surfaceFormat = device.physicalDevice.getSurfaceFormats().filterSurfaceFormat() - renderpass = device.simpleForwardRenderPass(surfaceFormat.format, vertexshader, fragmentshader, 2) - var (swapchain, res) = device.createSwapchain(renderpass, surfaceFormat, device.firstGraphicsQueue().get().family, 2) + renderPass = device.simpleForwardRenderPass(surfaceFormat.format, vertexshader, fragmentshader, 2) + var (swapchain, res) = device.createSwapchain(renderPass, surfaceFormat, device.firstGraphicsQueue().get().family, 2) if res != VK_SUCCESS: raise newException(Exception, "Unable to create swapchain") var thescene = Scene(root: newEntity("scene")) + thescene.setupDrawables(renderPass) - echo "All successfull" - for i in 0 ..< 2: - discard swapchain.drawNextFrame(thescene) + echo "Setup successfull, start rendering" + for i in 0 ..< 10: + discard swapchain.drawScene(thescene) echo "Rendered ", swapchain.framesRendered, " frames" echo "Start cleanup" @@ -90,7 +97,7 @@ checkVkResult device.vk.vkDeviceWaitIdle() vertexshader.destroy() fragmentshader.destroy() - renderpass.destroy() + renderPass.destroy() swapchain.destroy() device.destroy()