# HG changeset patch # User Sam # Date 1674402413 -25200 # Node ID a25325bec7f2256b991d7fa5935da1898046cc4d # Parent cd73e429fc996988ee49dbc8e31243fe40737ca8 did: deep refactoring of handling vertrex attribute and buffer updates, don't ask ;( diff -r cd73e429fc99 -r a25325bec7f2 README.md --- a/README.md Fri Jan 20 16:53:37 2023 +0700 +++ b/README.md Sun Jan 22 22:46:53 2023 +0700 @@ -41,8 +41,10 @@ - [ ] Depth buffering - [ ] Mipmaps - [ ] Multisampling -- [~] Instanced drawing (using it currently but number of instances is hardcoded to 1 +- [~] Instanced drawing (currently can use instance attributes, but we only support a single instance per mesh) - [ ] Fullscreen mode + switch between modes +- [ ] Fixed framerate +- [ ] Allow multipel Uniform blocks Build-system: - [x] move all of Makefile to config.nims @@ -71,3 +73,4 @@ - [ ] Animation system - [ ] Sprite system - [ ] Particle system +- [ ] Query and display rendering information from Vulkan diff -r cd73e429fc99 -r a25325bec7f2 examples/alotof_triangles.nim --- a/examples/alotof_triangles.nim Fri Jan 20 16:53:37 2023 +0700 +++ b/examples/alotof_triangles.nim Sun Jan 22 22:46:53 2023 +0700 @@ -2,7 +2,6 @@ import std/strutils import std/math import std/random -import std/enumerate import semicongine @@ -19,7 +18,8 @@ proc randomtransform(): Mat33 = let randomscale = scale2d(float32(rand(1.0) + 0.5), float32(rand(1.0) + 0.5)) let randomrotate = rotate2d(float32(rand(2 * PI))) - let randomtranslate = translate2d(float32(rand(1.6) - 0.8), float32(rand(1.6) - 0.8)) + let randomtranslate = translate2d(float32(rand(1.6) - 0.8), float32(rand( + 1.6) - 0.8)) result = randomtranslate * randomrotate * randomscale when isMainModule: @@ -27,11 +27,11 @@ var myengine = igniteEngine("A lot of triangles") const baseTriangle = [ Vec3([-0.1'f32, -0.1'f32, 1'f32]), - Vec3([ 0.1'f32, 0.1'f32, 1'f32]), - Vec3([-0.1'f32, 0.1'f32, 1'f32]), + Vec3([0.1'f32, 0.1'f32, 1'f32]), + Vec3([-0.1'f32, 0.1'f32, 1'f32]), ] - var scene = new Thing + var scene = newThing("scene") for i in 1 .. 300: var randommesh = new Mesh[VertexDataA] @@ -43,8 +43,8 @@ Vec2(transform1 * baseTriangle[0]), Vec2(transform1 * baseTriangle[1]), Vec2(transform1 * baseTriangle[2]), - ] - ), + ] + ), color22: ColorAttribute[Vec3]( data: @[randomcolor1, randomcolor1, randomcolor1] ) @@ -59,17 +59,14 @@ Vec2(transform2 * baseTriangle[0]), Vec2(transform2 * baseTriangle[1]), Vec2(transform2 * baseTriangle[2]), - ] - ), + ] + ), color22: ColorAttribute[Vec3]( data: @[randomcolor2, randomcolor2, randomcolor2] ) ) randomindexedmesh.indices = @[[0'u16, 1'u16, 2'u16]] - var childthing = new Thing - childthing.parts.add randommesh - childthing.parts.add randomindexedmesh - scene.children.add childthing + scene.add newThing("randommesh", randommesh, randomindexedmesh) const vertexShader = generateVertexShaderCode[VertexDataA, Uniforms]() const fragmentShader = generateFragmentShaderCode[VertexDataA]() diff -r cd73e429fc99 -r a25325bec7f2 examples/hello_cube.nim --- a/examples/hello_cube.nim Fri Jan 20 16:53:37 2023 +0700 +++ b/examples/hello_cube.nim Sun Jan 22 22:46:53 2023 +0700 @@ -6,11 +6,10 @@ # # # -# -# + + import std/times import std/strutils -import std/enumerate import semicongine @@ -26,35 +25,38 @@ var pipeline: RenderPipeline[VertexDataA, Uniforms] - uniforms:Uniforms + uniforms: Uniforms t: float32 proc globalUpdate(engine: var Engine, dt: float32) = - let ratio = float32(engine.vulkan.frameDimension.height) / float32(engine.vulkan.frameDimension.width) + let ratio = float32(engine.vulkan.frameDimension.height) / float32( + engine.vulkan.frameDimension.width) t += dt - uniforms.model.value = translate3d(0'f32, 0'f32, 10'f32) * rotate3d(t, Yf32) # * rotate3d(float32(PI), Yf32) + uniforms.model.value = translate3d(0'f32, 0'f32, 10'f32) * rotate3d(t, + Yf32) # * rotate3d(float32(PI), Yf32) uniforms.view.value = Unit44f32 - uniforms.projection.value = Mat44(data:[ + uniforms.projection.value = Mat44(data: [ ratio, 0'f32, 0'f32, 0'f32, 0'f32, 1'f32, 0'f32, 0'f32, 0'f32, 0'f32, 1'f32, 0'f32, 0'f32, 0'f32, 0'f32, 1'f32, ]) - uniforms.projection.value = perspective(float32(PI / 4), float32(engine.vulkan.frameDimension.width) / float32(engine.vulkan.frameDimension.height), 0.1'f32, 100'f32) - for buffer in pipeline.uniformBuffers: - buffer.updateData(uniforms) + uniforms.projection.value = perspective(float32(PI / 4), float32( + engine.vulkan.frameDimension.width) / float32( + engine.vulkan.frameDimension.height), 0.1'f32, 100'f32) + pipeline.updateUniformValues(uniforms) const - TopLeftFront = Vec3([ -0.5'f32, -0.5'f32, -0.5'f32]) - TopRightFront = Vec3([ 0.5'f32, -0.5'f32, -0.5'f32]) - BottomRightFront = Vec3([ 0.5'f32, 0.5'f32, -0.5'f32]) - BottomLeftFront = Vec3([ -0.5'f32, 0.5'f32, -0.5'f32]) - TopLeftBack = Vec3([ 0.5'f32, -0.5'f32, 0.5'f32]) - TopRightBack = Vec3([ -0.5'f32, -0.5'f32, 0.5'f32]) - BottomRightBack = Vec3([ -0.5'f32, 0.5'f32, 0.5'f32]) - BottomLeftBack = Vec3([ 0.5'f32, 0.5'f32, 0.5'f32]) + TopLeftFront = Vec3([-0.5'f32, -0.5'f32, -0.5'f32]) + TopRightFront = Vec3([0.5'f32, -0.5'f32, -0.5'f32]) + BottomRightFront = Vec3([0.5'f32, 0.5'f32, -0.5'f32]) + BottomLeftFront = Vec3([-0.5'f32, 0.5'f32, -0.5'f32]) + TopLeftBack = Vec3([0.5'f32, -0.5'f32, 0.5'f32]) + TopRightBack = Vec3([-0.5'f32, -0.5'f32, 0.5'f32]) + BottomRightBack = Vec3([-0.5'f32, 0.5'f32, 0.5'f32]) + BottomLeftBack = Vec3([0.5'f32, 0.5'f32, 0.5'f32]) const cube_pos = @[ TopLeftFront, TopRightFront, BottomRightFront, BottomLeftFront, # front @@ -78,18 +80,6 @@ let off = i * 4 tris.add [off + 0'u16, off + 1'u16, off + 2'u16] tris.add [off + 2'u16, off + 3'u16, off + 0'u16] -var off = 0'u16 * 4 -# tris.add [off + 0'u16, off + 1'u16, off + 2'u16] -# tris.add [off + 2'u16, off + 3'u16, off + 0'u16] -# off = 1'u16 * 4 -# tris.add [off + 0'u16, off + 1'u16, off + 2'u16] -# tris.add [off + 2'u16, off + 3'u16, off + 0'u16] -# off = 4'u16 * 4 -# tris.add [off + 0'u16, off + 1'u16, off + 2'u16] -# tris.add [off + 2'u16, off + 3'u16, off + 0'u16] -# off = 3'u16 * 4 -# tris.add [off + 0'u16, off + 1'u16, off + 2'u16] -# tris.add [off + 2'u16, off + 3'u16, off + 0'u16] when isMainModule: var myengine = igniteEngine("Hello cube") @@ -101,10 +91,7 @@ color: ColorAttribute[Vec3](data: cube_color), ) trianglemesh.indices = tris - # build a single-object scene graph - var triangle = new Thing - # add the triangle mesh to the object - triangle.parts.add trianglemesh + var cube = newThing("cube", trianglemesh) # upload data, prepare shaders, etc const vertexShader = generateVertexShaderCode[VertexDataA, Uniforms](""" @@ -113,7 +100,7 @@ const fragmentShader = generateFragmentShaderCode[VertexDataA]() pipeline = setupPipeline[VertexDataA, Uniforms, uint16]( myengine, - triangle, + cube, vertexShader, fragmentShader ) diff -r cd73e429fc99 -r a25325bec7f2 examples/hello_triangle.nim --- a/examples/hello_triangle.nim Fri Jan 20 16:53:37 2023 +0700 +++ b/examples/hello_triangle.nim Sun Jan 22 22:46:53 2023 +0700 @@ -9,7 +9,6 @@ VertexDataA = object position: PositionAttribute[Vec2] color: ColorAttribute[Vec3] - id: InstanceAttribute[Vec3] var pipeline: RenderPipeline[VertexDataA, void] @@ -19,9 +18,9 @@ # vertex data (types must match the above VertexAttributes) const triangle_pos = @[ - Vec2([ 0.0'f32, -0.5'f32]), - Vec2([ 0.5'f32, 0.5'f32]), - Vec2([-0.5'f32, 0.5'f32]), + Vec2([0.0'f32, -0.5'f32]), + Vec2([0.5'f32, 0.5'f32]), + Vec2([-0.5'f32, 0.5'f32]), ] triangle_color = @[ Vec3([1.0'f32, 0.0'f32, 0.0'f32]), @@ -37,12 +36,9 @@ trianglemesh.vertexData = VertexDataA( position: PositionAttribute[Vec2](data: triangle_pos), color: ColorAttribute[Vec3](data: triangle_color), - id: InstanceAttribute[Vec3](data: @[Vec3([0.5'f32, 0.5'f32, 0.5'f32])]), ) # build a single-object scene graph - var triangle = new Thing - # add the triangle mesh to the object - triangle.parts.add trianglemesh + var triangle = newThing("triangle", trianglemesh) # upload data, prepare shaders, etc const vertexShader = generateVertexShaderCode[VertexDataA, void]() diff -r cd73e429fc99 -r a25325bec7f2 examples/input.nim --- a/examples/input.nim Fri Jan 20 16:53:37 2023 +0700 +++ b/examples/input.nim Sun Jan 22 22:46:53 2023 +0700 @@ -1,6 +1,5 @@ +import std/strutils import std/times -import std/strutils -import std/enumerate import semicongine @@ -9,7 +8,12 @@ VertexDataA = object position: PositionAttribute[Vec2] color: ColorAttribute[Vec3] - iscursor: GenericAttribute[int32] + # transform: ModelTransformAttribute + # TODO: make this somehow a single vertex attribute + m1: GenericInstanceAttribute[Vec4] + m2: GenericInstanceAttribute[Vec4] + m3: GenericInstanceAttribute[Vec4] + m4: GenericInstanceAttribute[Vec4] Uniforms = object projection: Descriptor[Mat44] cursor: Descriptor[Vec2] @@ -17,30 +21,46 @@ var pipeline: RenderPipeline[VertexDataA, Uniforms] uniforms: Uniforms + scene: Thing + time: float proc globalUpdate(engine: var Engine, dt: float32) = - uniforms.cursor.value[0] = float32(engine.input.mouseX) - uniforms.cursor.value[1] = float32(engine.input.mouseY) + time += dt + uniforms.cursor.value = engine.input.mousePos uniforms.projection.value = ortho[float32]( - 0'f32, float32(engine.vulkan.frameDimension.width), - 0'f32, float32(engine.vulkan.frameDimension.height), + 0'f32, float32(engine.vulkan.frameSize.x), + 0'f32, float32(engine.vulkan.frameSize.y), 0'f32, 1'f32, ) - echo uniforms.projection.value - # echo uniforms.projection - for buffer in pipeline.uniformBuffers: - buffer.updateData(uniforms) + engine.vulkan.device.updateUniformData(pipeline, uniforms) -# vertex data (types must match the above VertexAttributes) + let cursor = firstPartWithName[Mesh[VertexDataA]](scene, "cursor") + if cursor != nil: + for c in cursor.vertexData.color.data.mitems: + c[1] = (sin(time * 8) * 0.5 + 0.5) * 0.2 + c[2] = (sin(time * 8) * 0.5 + 0.5) * 0.2 + engine.vulkan.device.updateVertexData(cursor.vertexData.color) + var trans = Unit44 * translate3d(engine.input.mousePos.x, + engine.input.mousePos.y, 0'f32) + cursor.vertexData.m1.data = @[trans.col(0)] + cursor.vertexData.m2.data = @[trans.col(1)] + cursor.vertexData.m3.data = @[trans.col(2)] + cursor.vertexData.m4.data = @[trans.col(3)] + engine.vulkan.device.updateVertexData(cursor.vertexData.m1) + engine.vulkan.device.updateVertexData(cursor.vertexData.m2) + engine.vulkan.device.updateVertexData(cursor.vertexData.m3) + engine.vulkan.device.updateVertexData(cursor.vertexData.m4) + + const shape = @[ - Vec2([- 1'f32, - 1'f32]), - Vec2([ 1'f32, - 1'f32]), + Vec2([ - 1'f32, - 1'f32]), + Vec2([1'f32, - 1'f32]), Vec2([-0.3'f32, -0.3'f32]), Vec2([-0.3'f32, -0.3'f32]), - Vec2([- 1'f32, 1'f32]), - Vec2([- 1'f32, - 1'f32]), + Vec2([ - 1'f32, 1'f32]), + Vec2([ - 1'f32, - 1'f32]), ] colors = @[ Vec3([1'f32, 0'f32, 0'f32]), @@ -54,13 +74,15 @@ when isMainModule: var myengine = igniteEngine("Input") - # build a single-object scene graph - var cursor = new Thing var cursormesh = new Mesh[VertexDataA] cursormesh.vertexData = VertexDataA( - position: PositionAttribute[Vec2](data: shape), + position: PositionAttribute[Vec2](data: shape, useOnDeviceMemory: true), color: ColorAttribute[Vec3](data: colors), - iscursor: GenericAttribute[int32](data: @[1'i32, 1'i32, 1'i32, 1'i32, 1'i32, 1'i32]), + # transform: ModelTransformAttribute(data: @[Unit44]), + m1: GenericInstanceAttribute[Vec4](data: @[Unit44.row(0)]), + m2: GenericInstanceAttribute[Vec4](data: @[Unit44.row(1)]), + m3: GenericInstanceAttribute[Vec4](data: @[Unit44.row(2)]), + m4: GenericInstanceAttribute[Vec4](data: @[Unit44.row(3)]), ) # transform the cursor a bit to make it look nice for i in 0 ..< cursormesh.vertexData.position.data.len: @@ -71,28 +93,40 @@ scale2d(0.5'f32, 1'f32) * rotate2d(float32(PI) / 4'f32) ) - let pos = Vec3([cursormesh.vertexData.position.data[i][0], cursormesh.vertexData.position.data[i][1], 1'f32]) + let pos = Vec3([cursormesh.vertexData.position.data[i][0], + cursormesh.vertexData.position.data[i][1], 1'f32]) cursormesh.vertexData.position.data[i] = (cursorscale * pos).xy - cursor.parts.add cursormesh - var box = new Thing var boxmesh = new Mesh[VertexDataA] boxmesh.vertexData = VertexDataA( position: PositionAttribute[Vec2](data: shape), color: ColorAttribute[Vec3](data: colors), - iscursor: GenericAttribute[int32](data: @[1'i32, 1'i32, 1'i32, 1'i32, 1'i32, 1'i32]), + # transform: ModelTransformAttribute(data: @[Unit44]), + m1: GenericInstanceAttribute[Vec4](data: @[Unit44.row(0)]), + m2: GenericInstanceAttribute[Vec4](data: @[Unit44.row(1)]), + m3: GenericInstanceAttribute[Vec4](data: @[Unit44.row(2)]), + m4: GenericInstanceAttribute[Vec4](data: @[Unit44.row(3)]), ) + for i in 0 ..< boxmesh.vertexData.position.data.len: + let boxscale = translate2d(100'f32, 100'f32) * scale2d(100'f32, 100'f32) + let pos = Vec3([boxmesh.vertexData.position.data[i][0], + boxmesh.vertexData.position.data[i][1], 1'f32]) + boxmesh.vertexData.position.data[i] = (boxscale * pos).xy + echo boxmesh.vertexData.position.data - var scene = new Thing - scene.children.add cursor + scene = newThing("scene") + scene.add newThing("cursor", cursormesh) + scene.add newThing("a box", boxmesh, newTransform(Unit44), newTransform( + translate3d(1'f32, 0'f32, 0'f32))) + scene.add newTransform(scale3d(1.5'f32, 1.5'f32, 1.5'f32)) # upload data, prepare shaders, etc const vertexShader = generateVertexShaderCode[VertexDataA, Uniforms](""" - out_position = uniforms.projection * vec4(in_position + (uniforms.cursor * iscursor), 0, 1); + mat4 mat = mat4(m1, m2, m3, m4); + out_position = uniforms.projection * mat * vec4(position, 0, 1); """) + echo vertexShader const fragmentShader = generateFragmentShaderCode[VertexDataA]() - echo vertexShader - echo fragmentShader pipeline = setupPipeline[VertexDataA, Uniforms, uint16]( myengine, scene, diff -r cd73e429fc99 -r a25325bec7f2 examples/squares.nim --- a/examples/squares.nim Fri Jan 20 16:53:37 2023 +0700 +++ b/examples/squares.nim Sun Jan 22 22:46:53 2023 +0700 @@ -2,7 +2,6 @@ import std/strutils import std/math import std/random -import std/enumerate import semicongine @@ -20,8 +19,7 @@ proc globalUpdate(engine: var Engine, dt: float32) = uniformdata.t.value += dt - for buffer in pipeline.uniformBuffers: - buffer.updateData(uniformdata) + pipeline.updateUniformValues(uniformdata) when isMainModule: randomize() @@ -57,11 +55,14 @@ iValues[vertIndex + 1] = uint32(squareIndex) iValues[vertIndex + 2] = uint32(squareIndex) iValues[vertIndex + 3] = uint32(squareIndex) - indices[squareIndex * 2 + 0] = [uint16(vertIndex + 0), uint16(vertIndex + 1), uint16(vertIndex + 2)] - indices[squareIndex * 2 + 1] = [uint16(vertIndex + 2), uint16(vertIndex + 3), uint16(vertIndex + 0)] + indices[squareIndex * 2 + 0] = [uint16(vertIndex + 0), uint16(vertIndex + + 1), uint16(vertIndex + 2)] + indices[squareIndex * 2 + 1] = [uint16(vertIndex + 2), uint16(vertIndex + + 3), uint16(vertIndex + 0)] - type PIndexedMesh = ref IndexedMesh[VertexDataA, uint16] # required so we can use ctor with ref/on heap + type PIndexedMesh = ref IndexedMesh[VertexDataA, + uint16] # required so we can use ctor with ref/on heap var squaremesh = PIndexedMesh( vertexData: VertexDataA( position11: PositionAttribute[Vec2](data: @vertices), @@ -70,10 +71,7 @@ ), indices: @indices ) - var scene = new Thing - var childthing = new Thing - childthing.parts.add squaremesh - scene.children.add childthing + var scene = newThing("scene", newThing("squares", squaremesh)) const vertexShader = generateVertexShaderCode[VertexDataA, Uniforms]( """ diff -r cd73e429fc99 -r a25325bec7f2 src/semicongine.nim --- a/src/semicongine.nim Fri Jan 20 16:53:37 2023 +0700 +++ b/src/semicongine.nim Sun Jan 22 22:46:53 2023 +0700 @@ -8,14 +8,16 @@ import semicongine/shader import semicongine/buffer import semicongine/events +import semicongine/window export vector export matrix export engine export vertex export descriptor +export thing export mesh -export thing export shader export buffer export events +export window diff -r cd73e429fc99 -r a25325bec7f2 src/semicongine/buffer.nim --- a/src/semicongine/buffer.nim Fri Jan 20 16:53:37 2023 +0700 +++ b/src/semicongine/buffer.nim Sun Jan 22 22:46:53 2023 +0700 @@ -1,3 +1,5 @@ +import std/typetraits + import ./vulkan import ./vulkan_helpers @@ -9,17 +11,24 @@ UniformBuffer = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT IndexBuffer = VK_BUFFER_USAGE_INDEX_BUFFER_BIT VertexBuffer = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT + MemoryProperty* = enum + DeviceLocal = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT + HostVisible = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT + HostCoherent = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT + MemoryProperties* = set[MemoryProperty] Buffer* = object device*: VkDevice vkBuffer*: VkBuffer size*: uint64 memoryRequirements*: VkMemoryRequirements + memoryProperties*: MemoryProperties memory*: VkDeviceMemory bufferTypes*: set[BufferType] - persistentMapping: bool - mapped: pointer + data*: pointer proc trash*(buffer: var Buffer) = + if int64(buffer.vkBuffer) != 0 and int64(buffer.memory) != 0: + vkUnmapMemory(buffer.device, buffer.memory) if int64(buffer.vkBuffer) != 0: vkDestroyBuffer(buffer.device, buffer.vkBuffer, nil) buffer.vkBuffer = VkBuffer(0) @@ -27,12 +36,13 @@ vkFreeMemory(buffer.device, buffer.memory, nil) buffer.memory = VkDeviceMemory(0) -proc findMemoryType(buffer: Buffer, physicalDevice: VkPhysicalDevice, properties: VkMemoryPropertyFlags): uint32 = +proc findMemoryType(buffer: Buffer, physicalDevice: VkPhysicalDevice, properties: MemoryProperties): uint32 = var physicalProperties: VkPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties(physicalDevice, addr(physicalProperties)) for i in 0'u32 ..< physicalProperties.memoryTypeCount: - if bool(buffer.memoryRequirements.memoryTypeBits and (1'u32 shl i)) and (uint32(physicalProperties.memoryTypes[i].propertyFlags) and uint32(properties)) == uint32(properties): + if bool(buffer.memoryRequirements.memoryTypeBits and (1'u32 shl i)): + if (uint32(physicalProperties.memoryTypes[i].propertyFlags) and cast[uint32](properties)) == cast[uint32](properties): return i proc InitBuffer*( @@ -40,10 +50,9 @@ physicalDevice: VkPhysicalDevice, size: uint64, bufferTypes: set[BufferType], - properties: set[VkMemoryPropertyFlagBits], - persistentMapping: bool = false + properties: MemoryProperties, ): Buffer = - result = Buffer(device: device, size: size, bufferTypes: bufferTypes, persistentMapping: persistentMapping) + result = Buffer(device: device, size: size, bufferTypes: bufferTypes, memoryProperties: properties) var usageFlags = 0 for usage in bufferTypes: usageFlags = ord(usageFlags) or ord(usage) @@ -56,46 +65,28 @@ checkVkResult vkCreateBuffer(result.device, addr(bufferInfo), nil, addr(result.vkBuffer)) vkGetBufferMemoryRequirements(result.device, result.vkBuffer, addr(result.memoryRequirements)) - var memoryProperties = 0'u32 - for prop in properties: - memoryProperties = memoryProperties or uint32(prop) - var allocInfo = VkMemoryAllocateInfo( sType: VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, allocationSize: result.memoryRequirements.size, - memoryTypeIndex: result.findMemoryType(physicalDevice, VkMemoryPropertyFlags(memoryProperties)) + memoryTypeIndex: result.findMemoryType(physicalDevice, properties) ) if result.size > 0: checkVkResult result.device.vkAllocateMemory(addr(allocInfo), nil, addr(result.memory)) checkVkResult result.device.vkBindBufferMemory(result.vkBuffer, result.memory, VkDeviceSize(0)) - if persistentMapping: - checkVkResult vkMapMemory( - result.device, - result.memory, - offset=VkDeviceSize(0), - VkDeviceSize(result.size), - VkMemoryMapFlags(0), - addr(result.mapped) - ) + checkVkResult vkMapMemory( + result.device, + result.memory, + offset=VkDeviceSize(0), + VkDeviceSize(result.size), + VkMemoryMapFlags(0), + addr(result.data) + ) -template withMapping*(buffer: Buffer, data: pointer, body: untyped): untyped = - assert not buffer.persistentMapping - checkVkResult vkMapMemory(buffer.device, buffer.memory, offset=VkDeviceSize(0), VkDeviceSize(buffer.size), VkMemoryMapFlags(0), addr(data)) - body - vkUnmapMemory(buffer.device, buffer.memory) - -# note: does not work with seq, because of sizeof -proc updateData*[T](buffer: Buffer, data: var T) = - if buffer.persistentMapping: - copyMem(buffer.mapped, addr(data), sizeof(T)) - else: - var p: pointer - buffer.withMapping(p): - copyMem(p, addr(data), sizeof(T)) - -proc copyBuffer*(commandPool: VkCommandPool, queue: VkQueue, src, dst: Buffer, size: uint64) = +proc transferBuffer*(commandPool: VkCommandPool, queue: VkQueue, src, dst: Buffer, size: uint64) = assert uint64(src.device) == uint64(dst.device) + assert TransferSrc in src.bufferTypes + assert TransferDst in dst.bufferTypes var allocInfo = VkCommandBufferAllocateInfo( sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, @@ -124,3 +115,4 @@ checkVkResult vkQueueSubmit(queue, 1, addr(submitInfo), VkFence(0)) checkVkResult vkQueueWaitIdle(queue) vkFreeCommandBuffers(src.device, commandPool, 1, addr(commandBuffer)) + diff -r cd73e429fc99 -r a25325bec7f2 src/semicongine/descriptor.nim --- a/src/semicongine/descriptor.nim Fri Jan 20 16:53:37 2023 +0700 +++ b/src/semicongine/descriptor.nim Sun Jan 22 22:46:53 2023 +0700 @@ -41,8 +41,7 @@ physicalDevice, uint64(size), {UniformBuffer}, - {VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT}, - persistentMapping=true, + {HostVisible, HostCoherent}, ) result[i] = buffer diff -r cd73e429fc99 -r a25325bec7f2 src/semicongine/engine.nim --- a/src/semicongine/engine.nim Fri Jan 20 16:53:37 2023 +0700 +++ b/src/semicongine/engine.nim Sun Jan 22 22:46:53 2023 +0700 @@ -1,3 +1,4 @@ +import std/options import std/times import std/typetraits import std/strformat @@ -5,6 +6,7 @@ import std/logging +import ./math/vector import ./vulkan import ./vulkan_helpers import ./window @@ -31,12 +33,14 @@ type Device = object - device: VkDevice - physicalDevice: PhysicalDevice - graphicsQueueFamily: uint32 - presentationQueueFamily: uint32 - graphicsQueue: VkQueue - presentationQueue: VkQueue + device*: VkDevice + physicalDevice*: PhysicalDevice + graphicsQueueFamily*: uint32 + presentationQueueFamily*: uint32 + graphicsQueue*: VkQueue + presentationQueue*: VkQueue + commandPool*: VkCommandPool + commandBuffers*: array[MAX_FRAMES_IN_FLIGHT, VkCommandBuffer] Swapchain = object swapchain: VkSwapchainKHR images: seq[VkImage] @@ -70,12 +74,10 @@ device*: Device surface*: VkSurfaceKHR surfaceFormat*: VkSurfaceFormatKHR - frameDimension*: VkExtent2D + frameSize*: TVec2[uint32] swapchain*: Swapchain framebuffers*: seq[VkFramebuffer] renderPass*: VkRenderPass - commandPool*: VkCommandPool - commandBuffers*: array[MAX_FRAMES_IN_FLIGHT, VkCommandBuffer] imageAvailableSemaphores*: array[MAX_FRAMES_IN_FLIGHT, VkSemaphore] renderFinishedSemaphores*: array[MAX_FRAMES_IN_FLIGHT, VkSemaphore] inFlightFences*: array[MAX_FRAMES_IN_FLIGHT, VkFence] @@ -86,12 +88,11 @@ mouseDown*: set[MouseButton] mousePressed*: set[MouseButton] mouseReleased*: set[MouseButton] - mouseX*: int - mouseY*: int + mousePos*: Vec2 Engine* = object vulkan*: Vulkan window*: NativeWindow - currentscenedata*: ref Thing + currentscenedata*: Thing input*: Input proc getAllPhysicalDevices(instance: VkInstance, surface: VkSurfaceKHR): seq[PhysicalDevice] = @@ -129,16 +130,16 @@ debug(&"Viable device: {cleanString(device.properties.deviceName)} (graphics queue family {graphicsQueueFamily}, presentation queue family {presentationQueueFamily})") -proc getFrameDimension(window: NativeWindow, device: VkPhysicalDevice, surface: VkSurfaceKHR): VkExtent2D = +proc getFrameDimension(window: NativeWindow, device: VkPhysicalDevice, surface: VkSurfaceKHR): TVec2[uint32] = let capabilities = device.getSurfaceCapabilities(surface) if capabilities.currentExtent.width != high(uint32): - return capabilities.currentExtent + return TVec2[uint32]([capabilities.currentExtent.width, capabilities.currentExtent.height]) else: let (width, height) = window.size() - return VkExtent2D( - width: min(max(uint32(width), capabilities.minImageExtent.width), capabilities.maxImageExtent.width), - height: min(max(uint32(height), capabilities.minImageExtent.height), capabilities.maxImageExtent.height), - ) + return TVec2[uint32]([ + min(max(uint32(width), capabilities.minImageExtent.width), capabilities.maxImageExtent.width), + min(max(uint32(height), capabilities.minImageExtent.height), capabilities.maxImageExtent.height), + ]) when DEBUG_LOG: proc setupDebugLog(instance: VkInstance): VkDebugUtilsMessengerEXT = @@ -176,12 +177,13 @@ result.presentationQueueFamily, ) -proc setupSwapChain(device: VkDevice, physicalDevice: PhysicalDevice, surface: VkSurfaceKHR, dimension: VkExtent2D, surfaceFormat: VkSurfaceFormatKHR): Swapchain = +proc setupSwapChain(device: VkDevice, physicalDevice: PhysicalDevice, surface: VkSurfaceKHR, dimension: TVec2[uint32], surfaceFormat: VkSurfaceFormatKHR): Swapchain = let capabilities = physicalDevice.device.getSurfaceCapabilities(surface) var selectedPresentationMode = getPresentMode(physicalDevice.presentModes) var imageCount = capabilities.minImageCount + 1 - imageCount = max(min(capabilities.maxImageCount, imageCount), 1) + if capabilities.maxImageCount > 0: + imageCount = min(capabilities.maxImageCount, imageCount) # setup swapchain var swapchainCreateInfo = VkSwapchainCreateInfoKHR( sType: VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, @@ -189,7 +191,7 @@ minImageCount: imageCount, imageFormat: surfaceFormat.format, imageColorSpace: surfaceFormat.colorSpace, - imageExtent: dimension, + imageExtent: VkExtent2D(width: dimension[0], height: dimension[1]), imageArrayLayers: 1, imageUsage: VkImageUsageFlags(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT), # VK_SHARING_MODE_CONCURRENT no supported (i.e cannot use different queue families for drawing to swap surface?) @@ -268,7 +270,7 @@ ) checkVkResult device.vkCreateRenderPass(addr(renderPassCreateInfo), nil, addr(result)) -proc initRenderPipeline[VertexType, Uniforms](device: VkDevice, frameDimension: VkExtent2D, renderPass: VkRenderPass, vertexShader, fragmentShader: static string): RenderPipeline[VertexType, Uniforms] = +proc initRenderPipeline[VertexType, Uniforms](device: VkDevice, frameSize: TVec2[uint32], renderPass: VkRenderPass, vertexShader, fragmentShader: static string): RenderPipeline[VertexType, Uniforms] = # load shaders result.device = device result.shaders.add(initShaderProgram[VertexType, Uniforms](device, VK_SHADER_STAGE_VERTEX_BIT, vertexShader)) @@ -396,7 +398,7 @@ addr(result.pipeline) ) -proc setupFramebuffers(device: VkDevice, swapchain: var Swapchain, renderPass: VkRenderPass, dimension: VkExtent2D): seq[VkFramebuffer] = +proc setupFramebuffers(device: VkDevice, swapchain: var Swapchain, renderPass: VkRenderPass, dimension: TVec2[uint32]): seq[VkFramebuffer] = result = newSeq[VkFramebuffer](swapchain.images.len) for i, imageview in enumerate(swapchain.imageviews): var framebufferInfo = VkFramebufferCreateInfo( @@ -404,8 +406,8 @@ renderPass: renderPass, attachmentCount: 1, pAttachments: addr(swapchain.imageviews[i]), - width: dimension.width, - height: dimension.height, + width: dimension[0], + height: dimension[1], layers: 1, ) checkVkResult device.vkCreateFramebuffer(addr(framebufferInfo), nil, addr(result[i])) @@ -418,9 +420,9 @@ device.vkDestroySwapchainKHR(swapchain.swapchain, nil) proc recreateSwapchain(vulkan: Vulkan): (Swapchain, seq[VkFramebuffer]) = - if vulkan.frameDimension.width == 0 or vulkan.frameDimension.height == 0: + if vulkan.frameSize.x == 0 or vulkan.frameSize.y == 0: return (vulkan.swapchain, vulkan.framebuffers) - debug(&"Recreate swapchain with dimension {vulkan.frameDimension}") + debug(&"Recreate swapchain with dimension {vulkan.frameSize}") checkVkResult vulkan.device.device.vkDeviceWaitIdle() vulkan.device.device.trash(vulkan.swapchain, vulkan.framebuffers) @@ -428,13 +430,13 @@ result[0] = vulkan.device.device.setupSwapChain( vulkan.device.physicalDevice, vulkan.surface, - vulkan.frameDimension, + vulkan.frameSize, vulkan.surfaceFormat ) result[1] = vulkan.device.device.setupFramebuffers( result[0], vulkan.renderPass, - vulkan.frameDimension + vulkan.frameSize ) @@ -473,6 +475,9 @@ proc igniteEngine*(windowTitle: string): Engine = result.window = createWindow(windowTitle) + let mousepos = result.window.getMousePosition() + if mousepos.isSome(): + result.input.mousePos = mousePos.get() # setup vulkan functions vkLoad1_0() @@ -488,24 +493,24 @@ # get basic frame information result.vulkan.surfaceFormat = result.vulkan.device.physicalDevice.formats.getSuitableSurfaceFormat() - result.vulkan.frameDimension = result.window.getFrameDimension(result.vulkan.device.physicalDevice.device, result.vulkan.surface) + result.vulkan.frameSize = result.window.getFrameDimension(result.vulkan.device.physicalDevice.device, result.vulkan.surface) # setup swapchain and render pipeline result.vulkan.swapchain = result.vulkan.device.device.setupSwapChain( result.vulkan.device.physicalDevice, result.vulkan.surface, - result.vulkan.frameDimension, + result.vulkan.frameSize, result.vulkan.surfaceFormat ) result.vulkan.renderPass = result.vulkan.device.device.setupRenderPass(result.vulkan.surfaceFormat.format) result.vulkan.framebuffers = result.vulkan.device.device.setupFramebuffers( result.vulkan.swapchain, result.vulkan.renderPass, - result.vulkan.frameDimension + result.vulkan.frameSize ) ( - result.vulkan.commandPool, - result.vulkan.commandBuffers, + result.vulkan.device.commandPool, + result.vulkan.device.commandBuffers, ) = result.vulkan.device.device.setupCommandBuffers(result.vulkan.device.graphicsQueueFamily) ( @@ -515,31 +520,24 @@ ) = result.vulkan.device.device.setupSyncPrimitives() -proc setupPipeline*[VertexType; UniformType; IndexType: uint16|uint32](engine: var Engine, scenedata: ref Thing, vertexShader, fragmentShader: static string): RenderPipeline[VertexType, UniformType] = +proc setupPipeline*[VertexType; UniformType; IndexType: uint16|uint32](engine: var Engine, scenedata: Thing, vertexShader, fragmentShader: static string): RenderPipeline[VertexType, UniformType] = engine.currentscenedata = scenedata result = initRenderPipeline[VertexType, UniformType]( engine.vulkan.device.device, - engine.vulkan.frameDimension, + engine.vulkan.frameSize, engine.vulkan.renderPass, vertexShader, fragmentShader, ) + # vertex buffers - var allmeshes: seq[Mesh[VertexType]] - for mesh in partsOfType[ref Mesh[VertexType]](engine.currentscenedata): - allmeshes.add(mesh[]) - if allmeshes.len > 0: - var ubermesh = createUberMesh(allmeshes) - result.vertexBuffers.add createVertexBuffers(ubermesh, result.device, engine.vulkan.device.physicalDevice.device, engine.vulkan.commandPool, engine.vulkan.device.graphicsQueue) + for mesh in allPartsOfType[Mesh[VertexType]](engine.currentscenedata): + result.vertexBuffers.add createVertexBuffers(mesh, result.device, engine.vulkan.device.physicalDevice.device, engine.vulkan.device.commandPool, engine.vulkan.device.graphicsQueue) + # vertex buffers with indexes when not (IndexType is void): - # vertex buffers with indexes - var allindexedmeshes: seq[IndexedMesh[VertexType, IndexType]] - for mesh in partsOfType[ref IndexedMesh[VertexType, IndexType]](engine.currentscenedata): - allindexedmeshes.add(mesh[]) - if allindexedmeshes.len > 0: - var indexedubermesh = createUberMesh(allindexedmeshes) - result.indexedVertexBuffers.add createIndexedVertexBuffers(indexedubermesh, result.device, engine.vulkan.device.physicalDevice.device, engine.vulkan.commandPool, engine.vulkan.device.graphicsQueue) + for mesh in allPartsOfType[IndexedMesh[VertexType, IndexType]](engine.currentscenedata): + result.indexedVertexBuffers.add createIndexedVertexBuffers(mesh, result.device, engine.vulkan.device.physicalDevice.device, engine.vulkan.device.commandPool, engine.vulkan.device.graphicsQueue) # uniform buffers when not (UniformType is void): @@ -592,6 +590,31 @@ ) vkUpdateDescriptorSets(result.device, 1, addr(descriptorWrite), 0, nil) +proc updateBufferData*[T](device: Device, buffer: Buffer, data: var T) = + when stripGenericParams(T) is seq: # seq needs special treatment for automated data uploading + assert data.len > 0 + let size = data.len * sizeof(get(genericParams(typeof(data)), 0)) + let dataptr = addr(data[0]) + else: + let size = sizeof(data) + let dataptr = addr(data) + if not (HostVisible in buffer.memoryProperties): + if not (TransferDst in buffer.bufferTypes): + raise newException(Exception, "Buffer cannot be updated") + var stagingBuffer = device.device.InitBuffer(device.physicalDevice.device, uint64(size), {TransferSrc}, {HostVisible, HostCoherent}) + copyMem(stagingBuffer.data, dataptr, size) + transferBuffer(device.commandPool, device.graphicsQueue, stagingBuffer, buffer, uint64(size)) + stagingBuffer.trash() + else: + copyMem(buffer.data, dataptr, size) + +proc updateVertexData*[T: VertexAttribute](device: Device, vertexAttribute: var T) = + device.updateBufferData(vertexAttribute.buffer, vertexAttribute.data) + +proc updateUniformData*[VertexType, Uniforms](device: Device, pipeline: RenderPipeline[VertexType, Uniforms], data: var Uniforms) = + for buffer in pipeline.uniformBuffers: + device.updateBufferData(buffer, data) + proc runPipeline[VertexType; Uniforms](commandBuffer: VkCommandBuffer, pipeline: var RenderPipeline[VertexType, Uniforms], currentFrame: int) = vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline) @@ -620,7 +643,7 @@ vkCmdBindIndexBuffer(commandBuffer, indexBuffer.vkBuffer, VkDeviceSize(0), indexType) vkCmdDrawIndexed(commandBuffer, indicesCount, 1, 0, 0, 0) -proc recordCommandBuffer(renderPass: VkRenderPass, pipeline: var RenderPipeline, commandBuffer: VkCommandBuffer, framebuffer: VkFramebuffer, frameDimension: VkExtent2D, currentFrame: int) = +proc recordCommandBuffer(renderPass: VkRenderPass, pipeline: var RenderPipeline, commandBuffer: VkCommandBuffer, framebuffer: VkFramebuffer, frameSize: TVec2[uint32], currentFrame: int) = var beginInfo = VkCommandBufferBeginInfo( sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, @@ -633,7 +656,7 @@ framebuffer: framebuffer, renderArea: VkRect2D( offset: VkOffset2D(x: 0, y: 0), - extent: frameDimension, + extent: VkExtent2D(width: frameSize.x, height: frameSize.y), ), clearValueCount: 1, pClearValues: addr(clearColor), @@ -641,14 +664,14 @@ viewport = VkViewport( x: 0.0, y: 0.0, - width: (float) frameDimension.width, - height: (float) frameDimension.height, + width: (float) frameSize.x, + height: (float) frameSize.y, minDepth: 0.0, maxDepth: 1.0, ) scissor = VkRect2D( offset: VkOffset2D(x: 0, y: 0), - extent: frameDimension + extent: VkExtent2D(width: frameSize.x, height: frameSize.y) ) checkVkResult vkBeginCommandBuffer(commandBuffer, addr(beginInfo)) block: @@ -671,14 +694,14 @@ addr(bufferImageIndex) ) if nextImageResult == VK_ERROR_OUT_OF_DATE_KHR: - vulkan.frameDimension = window.getFrameDimension(vulkan.device.physicalDevice.device, vulkan.surface) + vulkan.frameSize = window.getFrameDimension(vulkan.device.physicalDevice.device, vulkan.surface) (vulkan.swapchain, vulkan.framebuffers) = vulkan.recreateSwapchain() elif not (nextImageResult in [VK_SUCCESS, VK_SUBOPTIMAL_KHR]): raise newException(Exception, "Vulkan error: vkAcquireNextImageKHR returned " & $nextImageResult) checkVkResult vkResetFences(vulkan.device.device, 1, addr(vulkan.inFlightFences[currentFrame])) - checkVkResult vkResetCommandBuffer(vulkan.commandBuffers[currentFrame], VkCommandBufferResetFlags(0)) - vulkan.renderPass.recordCommandBuffer(pipeline, vulkan.commandBuffers[currentFrame], vulkan.framebuffers[bufferImageIndex], vulkan.frameDimension, currentFrame) + checkVkResult vkResetCommandBuffer(vulkan.device.commandBuffers[currentFrame], VkCommandBufferResetFlags(0)) + vulkan.renderPass.recordCommandBuffer(pipeline, vulkan.device.commandBuffers[currentFrame], vulkan.framebuffers[bufferImageIndex], vulkan.frameSize, currentFrame) var waitSemaphores = [vulkan.imageAvailableSemaphores[currentFrame]] waitStages = [VkPipelineStageFlags(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT)] @@ -689,7 +712,7 @@ pWaitSemaphores: addr(waitSemaphores[0]), pWaitDstStageMask: addr(waitStages[0]), commandBufferCount: 1, - pCommandBuffers: addr(vulkan.commandBuffers[currentFrame]), + pCommandBuffers: addr(vulkan.device.commandBuffers[currentFrame]), signalSemaphoreCount: 1, pSignalSemaphores: addr(signalSemaphores[0]), ) @@ -707,7 +730,7 @@ let presentResult = vkQueuePresentKHR(vulkan.device.presentationQueue, addr(presentInfo)) if presentResult == VK_ERROR_OUT_OF_DATE_KHR or presentResult == VK_SUBOPTIMAL_KHR or resized: - vulkan.frameDimension = window.getFrameDimension(vulkan.device.physicalDevice.device, vulkan.surface) + vulkan.frameSize = window.getFrameDimension(vulkan.device.physicalDevice.device, vulkan.surface) (vulkan.swapchain, vulkan.framebuffers) = vulkan.recreateSwapchain() @@ -743,10 +766,7 @@ engine.input.mouseReleased.incl event.button engine.input.mouseDown.excl event.button of MouseMoved: - engine.input.mouseX = event.x - engine.input.mouseY = event.y - else: - discard + engine.input.mousePos = Vec2([float32(event.x), float32(event.y)]) if killed: # at least on windows we should return immediately as swapchain recreation will fail after kill break @@ -756,8 +776,10 @@ dt = float32(float64((now - lastUpdate).inNanoseconds) / 1_000_000_000'f64) lastUpdate = now engine.globalUpdate(dt) - for entity in allEntities(engine.currentscenedata): - entity.update(dt) + for thing in allThings(engine.currentscenedata): + for part in thing.parts: + part.update(dt) + thing.update(dt) # submit frame for drawing engine.window.drawFrame(engine.vulkan, currentFrame, resized, pipeline) @@ -793,7 +815,7 @@ engine.vulkan.device.device.vkDestroyFence(engine.vulkan.inFlightFences[i], nil) engine.vulkan.device.device.vkDestroyRenderPass(engine.vulkan.renderPass, nil) - engine.vulkan.device.device.vkDestroyCommandPool(engine.vulkan.commandPool, nil) + engine.vulkan.device.device.vkDestroyCommandPool(engine.vulkan.device.commandPool, nil) engine.vulkan.instance.vkDestroySurfaceKHR(engine.vulkan.surface, nil) engine.vulkan.device.device.vkDestroyDevice(nil) diff -r cd73e429fc99 -r a25325bec7f2 src/semicongine/math/matrix.nim --- a/src/semicongine/math/matrix.nim Fri Jan 20 16:53:37 2023 +0700 +++ b/src/semicongine/math/matrix.nim Sun Jan 22 22:46:53 2023 +0700 @@ -81,9 +81,9 @@ generateAllConsts() -const Unit22* = unit22[float]() -const Unit33* = unit33[float]() -const Unit44* = unit44[float]() +const Unit22* = unit22[float32]() +const Unit33* = unit33[float32]() +const Unit44* = unit44[float32]() template rowCount*(m: typedesc): int = when m is TMat22: 2 diff -r cd73e429fc99 -r a25325bec7f2 src/semicongine/math/vector.nim --- a/src/semicongine/math/vector.nim Fri Jan 20 16:53:37 2023 +0700 +++ b/src/semicongine/math/vector.nim Sun Jan 22 22:46:53 2023 +0700 @@ -189,7 +189,7 @@ if accessorvalue.len == 0: raise newException(Exception, "empty attribute") elif accessorvalue.len == 1: - ret = nnkBracket.newTree(ident("value"), newLit(ACCESSOR_INDICES[accessorvalue[0]])) + ret = nnkBracketExpr.newTree(ident("value"), newLit(ACCESSOR_INDICES[accessorvalue[0]])) if accessorvalue.len > 1: var attrs = nnkBracket.newTree() for attrname in accessorvalue: diff -r cd73e429fc99 -r a25325bec7f2 src/semicongine/mesh.nim --- a/src/semicongine/mesh.nim Fri Jan 20 16:53:37 2023 +0700 +++ b/src/semicongine/mesh.nim Sun Jan 22 22:46:53 2023 +0700 @@ -1,3 +1,4 @@ +import std/options import std/typetraits import ./vulkan @@ -7,9 +8,9 @@ import ./math/vector type - Mesh*[T] = object of Part + Mesh*[T] = ref object of Part vertexData*: T - IndexedMesh*[T: object, U: uint16|uint32] = object of Part + IndexedMesh*[T: object, U: uint16|uint32] = ref object of Part vertexData*: T indices*: seq[array[3, U]] @@ -40,33 +41,32 @@ elif U is uint32: VK_INDEX_TYPE_UINT32 proc createVertexBuffers*[M: Mesh|IndexedMesh]( - mesh: var M, + mesh: M, device: VkDevice, physicalDevice: VkPhysicalDevice, commandPool: VkCommandPool, queue: VkQueue, - useDeviceLocalBuffer: bool = true # decides if data is transfered to the fast device-local memory or not ): (seq[Buffer], uint32) = result[1] = mesh.vertexData.VertexCount for name, value in mesh.vertexData.fieldPairs: when typeof(value) is VertexAttribute: assert value.data.len > 0 - var flags = if useDeviceLocalBuffer: {TransferSrc} else: {VertexBuffer} - var stagingBuffer = device.InitBuffer(physicalDevice, value.datasize, flags, {VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT}) - var d: pointer - stagingBuffer.withMapping(d): - copyMem(d, addr(value.data[0]), value.datasize) + var flags = if value.useOnDeviceMemory: {TransferSrc} else: {VertexBuffer} + var stagingBuffer = device.InitBuffer(physicalDevice, value.datasize, flags, {HostVisible, HostCoherent}) + copyMem(stagingBuffer.data, addr(value.data[0]), value.datasize) - if useDeviceLocalBuffer: - var finalBuffer = device.InitBuffer(physicalDevice, value.datasize, {TransferDst, VertexBuffer}, {VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT}) - copyBuffer(commandPool, queue, stagingBuffer, finalBuffer, value.datasize) + if value.useOnDeviceMemory: + var finalBuffer = device.InitBuffer(physicalDevice, value.datasize, {TransferDst, VertexBuffer}, {DeviceLocal}) + transferBuffer(commandPool, queue, stagingBuffer, finalBuffer, value.datasize) stagingBuffer.trash() result[0].add(finalBuffer) + value.buffer = finalBuffer else: result[0].add(stagingBuffer) + value.buffer = stagingBuffer proc createIndexBuffer*( - mesh: var IndexedMesh, + mesh: IndexedMesh, device: VkDevice, physicalDevice: VkPhysicalDevice, commandPool: VkCommandPool, @@ -76,29 +76,27 @@ let bufferSize = uint64(mesh.indices.len * sizeof(get(genericParams(typeof(mesh.indices)), 0))) let flags = if useDeviceLocalBuffer: {TransferSrc} else: {IndexBuffer} - var stagingBuffer = device.InitBuffer(physicalDevice, bufferSize, flags, {VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT}) - var d: pointer - stagingBuffer.withMapping(d): - copyMem(d, addr(mesh.indices[0]), bufferSize) + var stagingBuffer = device.InitBuffer(physicalDevice, bufferSize, flags, {HostVisible, HostCoherent}) + copyMem(stagingBuffer.data, addr(mesh.indices[0]), bufferSize) if useDeviceLocalBuffer: - var finalBuffer = device.InitBuffer(physicalDevice, bufferSize, {TransferDst, IndexBuffer}, {VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT}) - copyBuffer(commandPool, queue, stagingBuffer, finalBuffer, bufferSize) + var finalBuffer = device.InitBuffer(physicalDevice, bufferSize, {TransferDst, IndexBuffer}, {DeviceLocal}) + transferBuffer(commandPool, queue, stagingBuffer, finalBuffer, bufferSize) stagingBuffer.trash() return finalBuffer else: return stagingBuffer proc createIndexedVertexBuffers*( - mesh: var IndexedMesh, + mesh: IndexedMesh, device: VkDevice, physicalDevice: VkPhysicalDevice, commandPool: VkCommandPool, queue: VkQueue, - useDeviceLocalBuffer: bool = true # decides if data is transfered to the fast device-local memory or not + useDeviceLocalBufferForIndices: bool = true # decides if data is transfered to the fast device-local memory or not ): (seq[Buffer], Buffer, uint32, VkIndexType) = - result[0] = createVertexBuffers(mesh, device, physicalDevice, commandPool, queue, useDeviceLocalBuffer)[0] - result[1] = createIndexBuffer(mesh, device, physicalDevice, commandPool, queue, useDeviceLocalBuffer) + result[0] = createVertexBuffers(mesh, device, physicalDevice, commandPool, queue)[0] + result[1] = createIndexBuffer(mesh, device, physicalDevice, commandPool, queue, useDeviceLocalBufferForIndices) result[2] = uint32(mesh.indices.len * mesh.indices[0].len) result[3] = getVkIndexType(mesh) diff -r cd73e429fc99 -r a25325bec7f2 src/semicongine/platform/linux/xlib.nim --- a/src/semicongine/platform/linux/xlib.nim Fri Jan 20 16:53:37 2023 +0700 +++ b/src/semicongine/platform/linux/xlib.nim Sun Jan 22 22:46:53 2023 +0700 @@ -1,3 +1,4 @@ +import std/options import x11/xlib, x11/xutil, @@ -7,6 +8,7 @@ import x11/x import ../../events +import ../../math/vector import ./symkey_map @@ -92,3 +94,28 @@ result.add Event(eventType: ResizedWindow) else: discard + +proc getMousePosition*(window: NativeWindow): Option[Vec2] = + var + root: Window + win: Window + rootX: cint + rootY: cint + winX: cint + winY: cint + mask: cuint + onscreen = XQueryPointer( + window.display, + window.window, + addr(root), + addr(win), + addr(rootX), + addr(rootX), + addr(winX), + addr(winY), + addr(mask), + ) + if onscreen == 0: + return none(Vec2) + return some(Vec2([float32(winX), float32(winY)])) + diff -r cd73e429fc99 -r a25325bec7f2 src/semicongine/platform/windows/win32.nim --- a/src/semicongine/platform/windows/win32.nim Fri Jan 20 16:53:37 2023 +0700 +++ b/src/semicongine/platform/windows/win32.nim Sun Jan 22 22:46:53 2023 +0700 @@ -1,7 +1,9 @@ +import std/options import winim import ./virtualkey_map import ../../events +import ../../math/vector type NativeWindow* = object @@ -103,3 +105,10 @@ TranslateMessage(addr(msg)) DispatchMessage(addr(msg)) return currentEvents + +proc getMousePosition*(window: NativeWindow): Option[Vec2] = + var p: POINT + let res = GetCursorPos(addr(p)) + if res: + return some(Vec2([float32(p.x), float32(p.y)])) + return none(Vec2) diff -r cd73e429fc99 -r a25325bec7f2 src/semicongine/thing.nim --- a/src/semicongine/thing.nim Fri Jan 20 16:53:37 2023 +0700 +++ b/src/semicongine/thing.nim Sun Jan 22 22:46:53 2023 +0700 @@ -1,17 +1,80 @@ -{.experimental: "codeReordering".} +import std/strformat +import std/typetraits + +import ./vertex +import ./math/matrix type - Part* = object of RootObj - thing: ref Thing + Part* = ref object of RootObj + thing: Thing + Transform* = ref object of Part + mat: Mat44 + + Thing* = ref object of RootObj + name*: string + parent*: Thing + children*: seq[Thing] + parts*: seq[Part] + + +func `$`*(thing: Thing): string = thing.name +method `$`*(part: Part): string {.base.} = &"{part.thing} -> Part" +method `$`*(part: Transform): string = &"{part.thing} -> Transform" + +func newTransform*(mat: Mat44): Transform = + result = new Transform + result.mat = mat + +method update*(thing: Thing, dt: float32) {.base.} = discard +method update*(part: Part, dt: float32) {.base.} = discard - Thing* = object of RootObj - parent*: ref Thing - children*: seq[ref Thing] - parts*: seq[ref Part] +proc add*(thing: Thing, child: Thing) = + child.parent = thing + thing.children.add child +proc add*(thing: Thing, part: Part) = + part.thing = thing + thing.parts.add part +proc add*(thing: Thing, children: seq[Thing]) = + for child in children: + child.parent = thing + thing.children.add child +proc add*(thing: Thing, parts: seq[Part]) = + for part in parts: + part.thing = thing + thing.parts.add part -method update*(thing: ref Thing, dt: float32) {.base.} = discard +func newThing*(name: string=""): Thing = + result = new Thing + result.name = name + if result.name == "": + result.name = &"Thing[{$(cast[ByteAddress](result))}]" +func newThing*(name: string, firstChild: Thing, children: varargs[Thing]): Thing = + result = new Thing + result.add firstChild + for child in children: + result.add child + result.name = name + if result.name == "": + result.name = &"Thing[{$(cast[ByteAddress](result))}]" +proc newThing*(name: string, firstPart: Part, parts: varargs[Part]): Thing = + result = new Thing + result.name = name + result.add firstPart + for part in parts: + result.add part + if result.name == "": + result.name = &"Thing[{$(cast[ByteAddress](result))}]" -iterator partsOfType*[T: ref Part](root: ref Thing): T = +func getModelTransform*(thing: Thing): Mat44 = + result = Unit44 + var currentThing = thing + while currentThing != nil: + for part in currentThing.parts: + if part of Transform: + result = Transform(part).mat * result + currentThing = currentThing.parent + +iterator allPartsOfType*[T: Part](root: Thing): T = var queue = @[root] while queue.len > 0: let thing = queue.pop @@ -21,7 +84,47 @@ for child in thing.children: queue.insert(child, 0) -iterator allEntities*(root: ref Thing): ref Thing = +func firstWithName*(root: Thing, name: string): Thing = + var queue = @[root] + while queue.len > 0: + let next = queue.pop + for child in next.children: + if child.name == name: + return child + queue.add child + +func firstPartWithName*[T: Part](root: Thing, name: string): T = + var queue = @[root] + while queue.len > 0: + let next = queue.pop + for child in next.children: + if child.name == name: + for part in child.parts: + if part of T: + return T(part) + queue.add child + +func allWithName*(root: Thing, name: string): seq[Thing] = + var queue = @[root] + while queue.len > 0: + let next = queue.pop + for child in next.children: + if child.name == name: + result.add child + queue.add child + +func allPartsWithName*[T: Part](root: Thing, name: string): seq[T] = + var queue = @[root] + while queue.len > 0: + let next = queue.pop + for child in next.children: + if child.name == name: + for part in child.parts: + if part of T: + result.add T(part) + queue.add child + +iterator allThings*(root: Thing): Thing = var queue = @[root] while queue.len > 0: let next = queue.pop diff -r cd73e429fc99 -r a25325bec7f2 src/semicongine/vertex.nim --- a/src/semicongine/vertex.nim Fri Jan 20 16:53:37 2023 +0700 +++ b/src/semicongine/vertex.nim Sun Jan 22 22:46:53 2023 +0700 @@ -1,3 +1,4 @@ +import std/options import std/macros import std/strutils import std/strformat @@ -6,20 +7,31 @@ import ./math/vector import ./math/matrix import ./vulkan +import ./buffer import ./glsl_helpers type - VertexAttributeType = SomeNumber|TVec - AttributePurpose* = enum - Unknown, Position Color + VertexAttributeType = SomeNumber|TVec|TMat + # useOnDeviceMemory can be used to make sure the attribute buffer memory will + # be on the device. Data will be faster to access but much slower to update GenericAttribute*[T:VertexAttributeType] = object data*: seq[T] + buffer*: Buffer + useOnDeviceMemory*: bool PositionAttribute*[T:TVec] = object data*: seq[T] + buffer*: Buffer + useOnDeviceMemory*: bool ColorAttribute*[T:TVec] = object data*: seq[T] - InstanceAttribute*[T:TVec] = object + buffer*: Buffer + useOnDeviceMemory*: bool + GenericInstanceAttribute*[T:VertexAttributeType] = object data*: seq[T] + buffer*: Buffer + useOnDeviceMemory*: bool + ModelTransformAttribute* = GenericInstanceAttribute[Mat44] + InstanceAttribute* = GenericInstanceAttribute|ModelTransformAttribute VertexAttribute* = GenericAttribute|PositionAttribute|ColorAttribute|InstanceAttribute template getAttributeType*(v: VertexAttribute): auto = get(genericParams(typeof(v)), 0) @@ -29,14 +41,12 @@ # from https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap15.html func nLocationSlots[T: VertexAttributeType](): int = - when (T is TMat44[float64]): - 8 - elif (T is TMat44[float32]): - 4 - elif (T is TVec3[float64] or T is TVec3[uint64] or T is TVec4[float64] or T is TVec4[float64]): + when (T is TVec3[float64] or T is TVec3[uint64] or T is TVec4[float64] or T is TVec4[float64]): 2 + elif T is SomeNumber or T is TVec: + 1 else: - 1 + {.error: "Unsupported vertex attribute type".} # numbers func getVkFormat[T: VertexAttributeType](): VkFormat = @@ -80,6 +90,7 @@ 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".} @@ -91,6 +102,12 @@ else: assert result == uint32(value.data.len) +func hasAttributeType*[T, AT](t: T): uint32 = + for name, value in t.fieldPairs: + when typeof(value) is AT: + return true + return false + func generateGLSLVertexDeclarations*[T](): string = var stmtList: seq[string] var i = 0 diff -r cd73e429fc99 -r a25325bec7f2 tests/test_vector.nim --- a/tests/test_vector.nim Fri Jan 20 16:53:37 2023 +0700 +++ b/tests/test_vector.nim Sun Jan 22 22:46:53 2023 +0700 @@ -26,9 +26,12 @@ proc randVec2I(): auto = newVec2(rand(1 .. 10), rand(1 .. 10)) proc randVec2F(): auto = newVec2(rand(10'f) + 0.01, rand(10'f) + 0.01) proc randVec3I(): auto = newVec3(rand(1 .. 10), rand(1 .. 10), rand(1 .. 10)) -proc randVec3F(): auto = newVec3(rand(10'f) + 0.01, rand(10'f) + 0.01, rand(10'f) + 0.01) -proc randVec4I(): auto = newVec4(rand(1 .. 10), rand(1 .. 10), rand(1 .. 10), rand(1 .. 10)) -proc randVec4F(): auto = newVec4(rand(10'f) + 0.01, rand(10'f) + 0.01, rand(10'f) + 0.01, rand(10'f) + 0.01) +proc randVec3F(): auto = newVec3(rand(10'f) + 0.01, rand(10'f) + 0.01, rand( + 10'f) + 0.01) +proc randVec4I(): auto = newVec4(rand(1 .. 10), rand(1 .. 10), rand(1 .. 10), + rand(1 .. 10)) +proc randVec4F(): auto = newVec4(rand(10'f) + 0.01, rand(10'f) + 0.01, rand( + 10'f) + 0.01, rand(10'f) + 0.01) proc testVector() = @@ -134,6 +137,13 @@ echo "float2int ", to[int](randVec3F()) echo "int2float ", to[float](randVec3I()) + echo "V3I.x: ", randVec3I().x + echo "V3I.y: ", randVec3I().y + echo "V3F.z: ", randVec3F().z + echo "V3I.r: ", randVec3I().r + echo "V3I.g: ", randVec3I().g + echo "V3F.b: ", randVec3F().b + echo "V2I.xx: ", randVec2I().xx echo "V2I.yx: ", randVec2I().xy echo "V2F.xx: ", randVec2F().xx