Mercurial > games > semicongine
changeset 113:7b695fb335ed
did: first final implementation of scene-graph <-> pipeline connection, not working yet
author | Sam <sam@basx.dev> |
---|---|
date | Sun, 02 Apr 2023 01:22:09 +0700 |
parents | 0c5a74885796 |
children | 056e08dfad10 |
files | src/semicongine/gpu_data.nim src/semicongine/math/vector.nim src/semicongine/mesh.nim src/semicongine/platform/linux/xlib.nim src/semicongine/scene.nim src/semicongine/vulkan/buffer.nim src/semicongine/vulkan/renderpass.nim src/semicongine/vulkan/swapchain.nim src/semicongine/vulkan/utils.nim tests/test_vulkan_wrapper.nim |
diffstat | 10 files changed, 236 insertions(+), 55 deletions(-) [+] |
line wrap: on
line diff
--- a/src/semicongine/gpu_data.nim Sat Apr 01 00:40:02 2023 +0700 +++ b/src/semicongine/gpu_data.nim Sun Apr 02 01:22:09 2023 +0700 @@ -1,7 +1,9 @@ +import std/typetraits import std/strformat import std/tables import ./vulkan/api +import ./math type CountType = 1'u32 .. 4'u32 @@ -26,6 +28,16 @@ AttributeGroup* = object attributes*: seq[Attribute] +func vertexInputs*(group: AttributeGroup): seq[Attribute] = + for attr in group.attributes: + if attr.perInstance == false: + result.add attr + +func instanceInputs*(group: AttributeGroup): seq[Attribute] = + for attr in group.attributes: + if attr.perInstance == false: + result.add attr + 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) @@ -52,6 +64,47 @@ for attribute in thetype.attributes: result += attribute.size +func getDataType*[T: SomeNumber](value: T): DataType = + when T is float32: Float32 + elif T is float64: Float64 + elif T is int8: Int8 + elif T is int16: Int16 + elif T is int32: Int32 + elif T is int64: Int64 + elif T is uint8: UInt8 + elif T is uint16: UInt16 + elif T is uint32: UInt32 + elif T is uint64: UInt64 + elif T is int and sizeof(int) == sizeof(int64): Int64 + elif T is int and sizeof(int) == sizeof(int32): Int32 + elif T is uint and sizeof(uint) == sizeof(uint64): UInt64 + elif T is uint and sizeof(uint) == sizeof(uint32): UInt32 + elif T is float and sizeof(float) == sizeof(float32): Float32 + elif T is float and sizeof(float) == sizeof(float64): Float64 + else: + static: + raise newException(Exception, &"Unsupported data type for GPU data: {name(T)}" ) + +func asAttribute*[T: SomeNumber|TMat|TVec](value: T, name: string): Attribute = + when not (T is SomeNumber): + let nonScalarDatatype = getDataType(default(get(genericParams(typeof(value)), 0))) + + when T is SomeNumber: attr(name, getDataType(default(T))) + elif T is TMat22: attr(name, nonScalarDatatype, 2, 2) + elif T is TMat23: attr(name, nonScalarDatatype, 3, 2) + elif T is TMat32: attr(name, nonScalarDatatype, 3, 2) + elif T is TMat33: attr(name, nonScalarDatatype, 3, 3) + elif T is TMat34: attr(name, nonScalarDatatype, 4, 3) + elif T is TMat43: attr(name, nonScalarDatatype, 3, 4) + elif T is TMat44: attr(name, nonScalarDatatype, 4, 4) + elif T is TVec2: attr(name, nonScalarDatatype, 2) + elif T is TVec3: attr(name, nonScalarDatatype, 3) + elif T is TVec4: attr(name, nonScalarDatatype, 4) + else: {.error "Unsupported attribute type for GPU data".} + +func asAttribute*[T: SomeNumber|TMat|TVec](): Attribute = + asAttribute(default(T), name(T)) + const TYPEMAP = { CountType(1): { UInt8: VK_FORMAT_R8_UINT,
--- a/src/semicongine/math/vector.nim Sat Apr 01 00:40:02 2023 +0700 +++ b/src/semicongine/math/vector.nim Sun Apr 02 01:22:09 2023 +0700 @@ -12,12 +12,12 @@ TVec3*[T: SomeNumber] = array[3, T] TVec4*[T: SomeNumber] = array[4, T] TVec* = TVec2|TVec3|TVec4 - Vec2* = TVec2[float32] - Vec3* = TVec3[float32] - Vec4* = TVec4[float32] - Vec2I* = TVec2[uint32] - Vec3I* = TVec3[uint32] - Vec4I* = TVec4[uint32] + Vec2f* = TVec2[float32] + Vec3f* = TVec3[float32] + Vec4f* = TVec4[float32] + Vec2i* = TVec2[uint32] + Vec3i* = TVec3[uint32] + Vec4i* = TVec4[uint32] converter toVec2*[T: SomeNumber](orig: TVec3[T]|TVec4[T]): TVec2[T] = TVec2[T]([orig[0], orig[1]]) @@ -36,12 +36,12 @@ func ConstG[T: SomeNumber](): auto {.compiletime.} = TVec3[T]([T(0), T(1), T(0)]) func ConstB[T: SomeNumber](): auto {.compiletime.} = TVec3[T]([T(0), T(0), T(1)]) -func newVec2*(x=0'f32, y=0'f32): auto = - Vec2([x, y]) -func newVec3*(x=0'f32, y=0'f32, z=0'f32): auto = - Vec3([x, y, z]) -func newVec4*(x=0'f32, y=0'f32, z=0'f32, a=0'f32): auto = - Vec4([x, y, z, a]) +func newVec2f*(x=0'f32, y=0'f32): auto = + Vec2f([x, y]) +func newVec3f*(x=0'f32, y=0'f32, z=0'f32): auto = + Vec3f([x, y, z]) +func newVec4f*(x=0'f32, y=0'f32, z=0'f32, a=0'f32): auto = + Vec4f([x, y, z, a]) # generates constants: Xf, Xf32, Xf64, Xi, Xi8, Xi16, Xi32, Xi64 # Also for Y, Z, R, G, B and One @@ -65,12 +65,12 @@ generateAllConsts() -const X* = ConstX[float]() -const Y* = ConstY[float]() -const Z* = ConstZ[float]() -const One2* = ConstOne2[float]() -const One3* = ConstOne3[float]() -const One4* = ConstOne4[float]() +const X* = ConstX[float32]() +const Y* = ConstY[float32]() +const Z* = ConstZ[float32]() +const One2* = ConstOne2[float32]() +const One3* = ConstOne3[float32]() +const One4* = ConstOne4[float32]() func newVec2*[T](x, y: T): auto = TVec2([x, y]) func newVec3*[T](x, y, z: T): auto = TVec3([x, y, z])
--- a/src/semicongine/mesh.nim Sat Apr 01 00:40:02 2023 +0700 +++ b/src/semicongine/mesh.nim Sun Apr 02 01:22:09 2023 +0700 @@ -1,17 +1,34 @@ +import std/typetraits +import std/tables import std/enumerate import std/strformat import std/sequtils +import ./vulkan/utils +import ./gpu_data import ./entity import ./math type + SurfaceDataType = enum + Position, Color, Normal, Tangent, BiTangent, TextureCoordinate, Index, BigIndex MeshIndexType* = enum None Small # up to 2^16 vertices Big # up to 2^32 vertices + MeshData = object + case thetype*: SurfaceDataType + of Position: position: seq[Vec3f] + of Color: color: seq[Vec3f] + of Normal: normal: seq[Vec3f] + of Tangent: tangent: seq[Vec3f] + of BiTangent: bitangent: seq[Vec3f] + of TextureCoordinate: texturecoord: seq[Vec2f] + of Index: index: seq[uint16] + of BigIndex: bigindex: seq[uint32] Mesh* = ref object of Component - vertices: seq[Vec3] + vertexCount*: uint32 + data: Table[Attribute, MeshData] case indexType*: MeshIndexType of None: discard @@ -20,29 +37,78 @@ of Big: bigIndices*: seq[array[3, uint32]] + method `$`*(mesh: Mesh): string = - &"Mesh ({mesh.vertices.len})" + &"Mesh ({mesh.vertexCount})" -func newMesh*(vertices: openArray[Vec3]): auto = - Mesh(vertices: vertices.toSeq, indexType: None) +func newMesh*(vertices: openArray[Vec3f]): auto = + let meshdata = {asAttribute(default(Vec3f), "position"): MeshData(thetype: Position, position: vertices.toSeq)}.toTable + Mesh(vertexCount: uint32(vertices.len), data: meshdata, indexType: None) -func newMesh*(vertices: openArray[Vec3], indices: openArray[array[3, uint32|int32]]): auto = +func newMesh*(vertices: openArray[Vec3f], indices: openArray[array[3, uint32|int32]]): auto = + let meshdata = {asAttribute(default(Vec3f), "position"): MeshData(thetype: Position, position: vertices.toSeq)}.toTable if uint16(vertices.len) < high(uint16): var smallIndices = newSeq[array[3, uint16]](indices.len) for i, tri in enumerate(indices): smallIndices[i] = [uint16(tri[0]), uint16(tri[1]), uint16(tri[3])] - Mesh(vertices: vertices.toSeq, indexType: Small, smallIndices: smallIndices) + Mesh(vertexCount: uint32(vertices.len), data: meshdata, indexType: Small, smallIndices: smallIndices) else: var bigIndices = newSeq[array[3, uint32]](indices.len) for i, tri in enumerate(indices): bigIndices[i] = [uint32(tri[0]), uint32(tri[1]), uint32(tri[3])] - Mesh(vertices: vertices.toSeq, indexType: Big, bigIndices: bigIndices) + Mesh(vertexCount: uint32(vertices.len), data: meshdata, indexType: Big, bigIndices: bigIndices) -func newMesh*(vertices: openArray[Vec3], indices: openArray[array[3, uint16|int16]]): auto = +func newMesh*(vertices: openArray[Vec3f], indices: openArray[array[3, uint16|int16]]): auto = + let meshdata = {asAttribute(default(Vec3f), "position"): MeshData(thetype: Position, position: vertices.toSeq)}.toTable var smallIndices = newSeq[array[3, uint16]](indices.len) for i, tri in enumerate(indices): smallIndices[i] = [uint16(tri[0]), uint16(tri[1]), uint16(tri[3])] - Mesh(vertices: vertices.toSeq, indexType: Small, smallIndices: smallIndices) + Mesh(vertexCount: vertices.len, data: meshdata, indexType: Small, smallIndices: smallIndices) + + +func size*(meshdata: MeshData): uint64 = + case meshdata.thetype: + of Position: meshdata.position.size + of Color: meshdata.color.size + of Normal: meshdata.normal.size + of Tangent: meshdata.tangent.size + of BiTangent: meshdata.bitangent.size + of TextureCoordinate: meshdata.texturecoord.size + of Index: meshdata.index.size + of BigIndex: meshdata.bigindex.size + +func size*(mesh: Mesh, attribute: Attribute): uint64 = + mesh.data[attribute].size + +func size*(mesh: Mesh): uint64 = + for d in mesh.data.values: + result += d.size + +proc rawData[T: seq](value: var T): (pointer, uint64) = + (pointer(addr(value)), uint64(sizeof(get(genericParams(typeof(value)), 0)) * value.len)) + +proc getRawData(data: var MeshData): (pointer, uint64) = + case data.thetype: + of Position: rawData(data.position) + of Color: rawData(data.color) + of Normal: rawData(data.normal) + of Tangent: rawData(data.tangent) + of BiTangent: rawData(data.bitangent) + of TextureCoordinate: rawData(data.texturecoord) + of Index: rawData(data.index) + of BigIndex: rawData(data.bigindex) + +proc hasDataFor*(mesh: Mesh, attribute: Attribute): bool = + assert attribute.perInstance == false, "Mesh data cannot handle per-instance attributes" + attribute in mesh.data + +proc getRawData*(mesh: Mesh, attribute: Attribute): (pointer, uint64) = + assert attribute.perInstance == false, "Mesh data cannot handle per-instance attributes" + mesh.data[attribute].getRawData() + +proc getData*(mesh: Mesh, attribute: Attribute): MeshData = + assert attribute.perInstance == false, "Mesh data cannot handle per-instance attributes" + mesh.data[attribute] #[ @@ -76,8 +142,7 @@ for name, value in mesh.vertexData.fieldPairs: assert value.data.len > 0 var flags = if value.useOnDeviceMemory: {TransferSrc} else: {VertexBuffer} - var stagingBuffer = device.InitBuffer(physicalDevice, value.datasize, - flags, {HostVisible, HostCoherent}) + var stagingBuffer = device.InitBuffer(physicalDevice, value.datasize, flags, {HostVisible, HostCoherent}) copyMem(stagingBuffer.data, addr(value.data[0]), value.datasize) if value.useOnDeviceMemory:
--- a/src/semicongine/platform/linux/xlib.nim Sat Apr 01 00:40:02 2023 +0700 +++ b/src/semicongine/platform/linux/xlib.nim Sun Apr 02 01:22:09 2023 +0700 @@ -124,7 +124,7 @@ result.add events[0] -proc getMousePosition*(window: NativeWindow): Option[Vec2] = +proc getMousePosition*(window: NativeWindow): Option[Vec2f] = var root: x.Window win: x.Window @@ -144,7 +144,6 @@ addr(winY), addr(mask), ) - if onscreen == 0: - return none(Vec2) - return some(Vec2([float32(winX), float32(winY)])) + if onscreen != 0: + result = some(Vec2f([float32(winX), float32(winY)]))
--- a/src/semicongine/scene.nim Sat Apr 01 00:40:02 2023 +0700 +++ b/src/semicongine/scene.nim Sun Apr 02 01:22:09 2023 +0700 @@ -1,15 +1,17 @@ import std/tables +import std/strformat import ./vulkan/api import ./vulkan/buffer import ./vulkan/pipeline import ./vulkan/renderpass +import ./gpu_data import ./entity import ./mesh type Drawable* = object - buffers*: seq[(Buffer, int)] # buffer + offset from buffer + buffers*: seq[(Buffer, uint64)] # buffer + offset from buffer elementCount*: uint32 # number of vertices or indices instanceCount*: uint32 # number of instance case indexed*: bool @@ -24,23 +26,51 @@ root*: Entity drawables: Table[VkPipeline, seq[Drawable]] +func `$`*(drawable: Drawable): string = + if drawable.indexed: + &"Drawable(elementCount: {drawable.elementCount}, instanceCount: {drawable.instanceCount}, buffers: {drawable.buffers}, indexType: {drawable.indexType})" + else: + &"Drawable(elementCount: {drawable.elementCount}, instanceCount: {drawable.instanceCount}, buffers: {drawable.buffers})" + proc setupDrawables(scene: var Scene, pipeline: Pipeline) = - var meshes: seq[Mesh] - var smallIMeshes: seq[Mesh] - var bigIMeshes: seq[Mesh] + assert pipeline.device.vk.valid + assert not (pipeline.vk in scene.drawables) + + scene.drawables[pipeline.vk] = @[] + + var + nonIMeshes: seq[Mesh] + smallIMeshes: seq[Mesh] + bigIMeshes: seq[Mesh] for mesh in allPartsOfType[Mesh](scene.root): + for inputAttr in pipeline.inputs.vertexInputs: + assert mesh.hasDataFor(inputAttr), &"{mesh} missing data for {inputAttr}" case mesh.indexType: - of None: meshes.add mesh + of None: nonIMeshes.add mesh of Small: smallIMeshes.add mesh of Big: bigIMeshes.add mesh - echo pipeline.inputs - - # one drawable per mesh list - # one buffer per pipeline.input - # how to find data for pipeline.inputs attribute-buffer? - # position: get from mesh, mark attribute - # color/UVs: material component? - scene.drawables[pipeline.vk] = @[] + + if nonIMeshes.len > 0: + var + bufferSize = 0'u64 + vertexCount = 0'u32 + for mesh in nonIMeshes: + bufferSize += mesh.size + vertexCount += mesh.vertexCount + var buffer = pipeline.device.createBuffer( + size=bufferSize, + usage=[VK_BUFFER_USAGE_VERTEX_BUFFER_BIT], + memoryFlags=[VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT], + ) + var offset = 0'u64 + var drawable = Drawable(elementCount: vertexCount, indexed: false, instanceCount: 1) + for inputAttr in pipeline.inputs.vertexInputs: + drawable.buffers.add (buffer, offset) + for mesh in nonIMeshes: + var (pdata, size) = mesh.getRawData(inputAttr) + buffer.setData(pdata, size, offset) + offset += size + scene.drawables[pipeline.vk].add drawable #[ proc createVertexBuffers*[M: Mesh]( @@ -76,3 +106,12 @@ proc getDrawables*(scene: Scene, pipeline: Pipeline): seq[Drawable] = scene.drawables.getOrDefault(pipeline.vk, @[]) + +proc destroy*(scene: var Scene) = + for drawables in scene.drawables.mvalues: + for drawable in drawables.mitems: + for (buffer, offset) in drawable.buffers.mitems: + # if buffer.vk.valid: # required because we allow duplicates in drawable.buffers + buffer.destroy() + if drawable.indexed: + drawable.indexBuffer.destroy()
--- a/src/semicongine/vulkan/buffer.nim Sat Apr 01 00:40:02 2023 +0700 +++ b/src/semicongine/vulkan/buffer.nim Sun Apr 02 01:22:09 2023 +0700 @@ -1,3 +1,5 @@ +import std/strformat +import std/typetraits import std/sequtils import std/tables @@ -19,6 +21,18 @@ memory*: DeviceMemory data*: pointer +func `$`*(buffer: Buffer): string = &"Buffer(size: {buffer.size}, usage: {buffer.usage})" + +proc setData*(dst: Buffer, src: pointer, len: uint64, offset=0'u64) = + assert offset + len <= dst.size + copyMem(cast[pointer](cast[uint64](dst.data) + offset), src, len) + +proc setData*[T: seq](dst: Buffer, src: ptr T, offset=0'u64) = + dst.setData(src, sizeof(get(genericParams(T), 0)) * src[].len, offset=offset) + +proc setData*[T](dst: Buffer, src: ptr T, offset=0'u64) = + dst.setData(src, sizeof(T), offset=offset) + proc allocateMemory(buffer: var Buffer, flags: openArray[VkMemoryPropertyFlagBits]) = assert buffer.device.vk.valid assert buffer.hasMemory == false @@ -33,9 +47,9 @@ proc createBuffer*( device: Device, size: uint64, - flags: openArray[VkBufferCreateFlagBits], usage: openArray[VkBufferUsageFlagBits], - memoryFlags: openArray[VkMemoryPropertyFlagBits], + flags: openArray[VkBufferCreateFlagBits] = @[], + memoryFlags: openArray[VkMemoryPropertyFlagBits] = @[], ): Buffer = assert device.vk.valid assert size > 0
--- a/src/semicongine/vulkan/renderpass.nim Sat Apr 01 00:40:02 2023 +0700 +++ b/src/semicongine/vulkan/renderpass.nim Sun Apr 02 01:22:09 2023 +0700 @@ -12,7 +12,7 @@ type Subpass* = object - clearColor*: Vec4 + clearColor*: Vec4f pipelineBindPoint*: VkPipelineBindPoint flags: VkSubpassDescriptionFlags inputs: seq[VkAttachmentReference] @@ -199,7 +199,7 @@ pipeline.descriptorSets = pipeline.descriptorPool.allocateDescriptorSet(pipeline.descriptorSetLayout, renderPass.inFlightFrames) renderPass.subpasses[subpass].pipelines.add pipeline -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 = +proc simpleForwardRenderPass*(device: Device, format: VkFormat, vertexShader: Shader, fragmentShader: Shader, inFlightFrames: int, clearColor=Vec4f([0.8'f32, 0.8'f32, 0.8'f32, 1'f32])): RenderPass = assert device.vk.valid var attachments = @[VkAttachmentDescription(
--- a/src/semicongine/vulkan/swapchain.nim Sat Apr 01 00:40:02 2023 +0700 +++ b/src/semicongine/vulkan/swapchain.nim Sun Apr 02 01:22:09 2023 +0700 @@ -1,4 +1,5 @@ import std/options +import std/logging import ./api import ./utils @@ -156,6 +157,7 @@ proc draw*(commandBuffer: VkCommandBuffer, drawables: seq[Drawable], scene: Scene) = for drawable in drawables: + debug "Draw ", drawable var buffers: seq[VkBuffer] var offsets: seq[VkDeviceSize] for (buffer, offset) in drawable.buffers:
--- a/src/semicongine/vulkan/utils.nim Sat Apr 01 00:40:02 2023 +0700 +++ b/src/semicongine/vulkan/utils.nim Sun Apr 02 01:22:09 2023 +0700 @@ -1,3 +1,4 @@ +import std/typetraits import std/strutils import std/strformat @@ -18,4 +19,5 @@ raise newException(Exception, &"Running '{command}' produced exit code: {exitcode}" & output) return output - +func size*[T: seq](list: T): uint64 = + uint64(list.len * sizeof(get(genericParams(typeof(list)), 0)))
--- a/tests/test_vulkan_wrapper.nim Sat Apr 01 00:40:02 2023 +0700 +++ b/tests/test_vulkan_wrapper.nim Sun Apr 02 01:22:09 2023 +0700 @@ -58,12 +58,12 @@ selectedPhysicalDevice.filterForGraphicsPresentationQueues() ) - const inputs = AttributeGroup(attributes: @[attr(name="pos", thetype=Float32, components=3)]) + const inputs = AttributeGroup(attributes: @[attr(name="position", 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);") + const vertexBinary = shaderCode(inputs=inputs, uniforms=uniforms, outputs=outputs, stage=VK_SHADER_STAGE_VERTEX_BIT, version=450, entrypoint="main", "fragpos = position;") + const fragmentBinary = shaderCode(inputs=outputs, uniforms=uniforms, outputs=fragOutput, stage=VK_SHADER_STAGE_FRAGMENT_BIT, version=450, entrypoint="main", "color = vec4(1, 1, 1, 1);") var 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) @@ -73,7 +73,13 @@ if res != VK_SUCCESS: raise newException(Exception, "Unable to create swapchain") - var thescene = Scene(name: "main", root: newEntity("triangle", newMesh([newVec3(-1, -1), newVec3(0, 1), newVec3(1, -1)]))) + var thescene = Scene( + name: "main", + root: newEntity("root", + newEntity("triangle1", newMesh([newVec3f(-0.5, -0.5), newVec3f(0.5, 0.5), newVec3f(0.5, -0.5)])), + newEntity("triangle2", newMesh([newVec3f(-0.5, -0.5), newVec3f(0.5, -0.5), newVec3f(0.5, 0.5)])), + ) + ) thescene.setupDrawables(renderPass) echo "Setup successfull, start rendering" @@ -85,6 +91,7 @@ # cleanup checkVkResult device.vk.vkDeviceWaitIdle() + thescene.destroy() vertexshader.destroy() fragmentshader.destroy() renderPass.destroy()