changeset 1321:385dbd68a947

did: a TON of copy elimination, some tests run now waaaay faster
author sam <sam@basx.dev>
date Thu, 15 Aug 2024 12:12:27 +0700
parents 19e3eedb9a41
children 4a1c2b1128bc
files semicongine/audio/mixer.nim semicongine/core/utils.nim semicongine/gltf.nim semicongine/image.nim semicongine/rendering.nim semicongine/rendering/renderer.nim semicongine/rendering/shaders.nim semicongine/text/textbox.nim tests/test_gltf.nim tests/test_text.nim
diffstat 10 files changed, 74 insertions(+), 54 deletions(-) [+]
line wrap: on
line diff
--- a/semicongine/audio/mixer.nim	Wed Aug 14 20:06:51 2024 +0700
+++ b/semicongine/audio/mixer.nim	Thu Aug 15 12:12:27 2024 +0700
@@ -29,6 +29,9 @@
     fadeTime: float
     fadeStep: float
 
+proc `=copy`(dest: var Playback; source: Playback) {.error.}
+proc `=copy`(dest: var Track; source: Track) {.error.}
+
 when defined(windows):
   include ./platform/windows
 when defined(linux):
@@ -46,11 +49,14 @@
     currentBuffer: int
     lastUpdate: MonoTime
 
+proc `=copy`(dest: var Mixer; source: Mixer) {.error.}
+
 proc initMixer(): Mixer =
   result = Mixer(
-    tracks: {"": Track(level: 1'f)}.toTable,
+    tracks: initTable[string, Track](),
     level: 1'f,
   )
+  result.tracks[""] = Track(level: 1)
   result.lock.initLock()
 
 proc setupDevice(mixer: var Mixer) =
--- a/semicongine/core/utils.nim	Wed Aug 14 20:06:51 2024 +0700
+++ b/semicongine/core/utils.nim	Thu Aug 15 12:12:27 2024 +0700
@@ -51,3 +51,13 @@
     echo name, ": ", (getMonoTime() - t0).inNanoseconds.float / 1_000_000, "ms"
   else:
     body
+
+# allow enforcing use of iterators with lent
+iterator litems*[IX, T](a: array[IX, T]): lent T {.inline.} =
+  ## Iterates over each item of `a`.
+  when a.len > 0:
+    var i = low(IX)
+    while true:
+      yield a[i]
+      if i >= high(IX): break
+      inc(i)
--- a/semicongine/gltf.nim	Wed Aug 14 20:06:51 2024 +0700
+++ b/semicongine/gltf.nim	Thu Aug 15 12:12:27 2024 +0700
@@ -60,6 +60,11 @@
     indices*: string
     material*: string
 
+proc `=copy`(dest: var GltfNode; source: GltfNode) {.error.}
+proc `=copy`(dest: var glTFHeader; source: glTFHeader) {.error.}
+proc `=copy`(dest: var glTFData; source: glTFData) {.error.}
+proc `=copy`[S, T](dest: var GltfData[S, T]; source: GltfData[S, T]) {.error.}
+
 const
   HEADER_MAGIC = 0x46546C67
   JSON_CHUNK = 0x4E4F534A
--- a/semicongine/image.nim	Wed Aug 14 20:06:51 2024 +0700
+++ b/semicongine/image.nim	Thu Aug 15 12:12:27 2024 +0700
@@ -52,7 +52,6 @@
     channels_in_file = addr(c),
     desired_channels = nChannels
   )
-  # if lodepng_decode_memory(out_data = addr(data), w = addr(w), h = addr(h), in_data = cast[cstring](pngData.ToCPointer), insize = csize_t(pngData.len), colorType = pngType, bitdepth = 8) != 0:
   if data == nil:
     raise newException(Exception, "An error occured while loading PNG file")
 
--- a/semicongine/rendering.nim	Wed Aug 14 20:06:51 2024 +0700
+++ b/semicongine/rendering.nim	Thu Aug 15 12:12:27 2024 +0700
@@ -48,6 +48,9 @@
   include ./rendering/platform/linux
 
 type
+  # type aliases
+  SupportedGPUType = float32 | float64 | int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | TVec2[int32] | TVec2[int64] | TVec3[int32] | TVec3[int64] | TVec4[int32] | TVec4[int64] | TVec2[uint32] | TVec2[uint64] | TVec3[uint32] | TVec3[uint64] | TVec4[uint32] | TVec4[uint64] | TVec2[float32] | TVec2[float64] | TVec3[float32] | TVec3[float64] | TVec4[float32] | TVec4[float64] | TMat2[float32] | TMat2[float64] | TMat23[float32] | TMat23[float64] | TMat32[float32] | TMat32[float64] | TMat3[float32] | TMat3[float64] | TMat34[float32] | TMat34[float64] | TMat43[float32] | TMat43[float64] | TMat4[float32] | TMat4[float64]
+
   VulkanGlobals* = object
     # populated through InitVulkan proc
     instance*: VkInstance
@@ -95,13 +98,6 @@
     oldSwapchain: Swapchain
     oldSwapchainCounter: int # swaps until old swapchain will be destroyed
 
-var vulkan*: VulkanGlobals
-var fullscreen_internal: bool
-
-type
-  # type aliases
-  SupportedGPUType = float32 | float64 | int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | TVec2[int32] | TVec2[int64] | TVec3[int32] | TVec3[int64] | TVec4[int32] | TVec4[int64] | TVec2[uint32] | TVec2[uint64] | TVec3[uint32] | TVec3[uint64] | TVec4[uint32] | TVec4[uint64] | TVec2[float32] | TVec2[float64] | TVec3[float32] | TVec3[float64] | TVec4[float32] | TVec4[float64] | TMat2[float32] | TMat2[float64] | TMat23[float32] | TMat23[float64] | TMat32[float32] | TMat32[float64] | TMat3[float32] | TMat3[float64] | TMat34[float32] | TMat34[float64] | TMat43[float32] | TMat43[float64] | TMat4[float32] | TMat4[float64]
-
   # shader related types
   DescriptorSet*[T: object] = object
     data*: T
@@ -114,11 +110,6 @@
     descriptorSetLayouts*: array[MAX_DESCRIPTORSETS, VkDescriptorSetLayout]
 
   # memory/buffer related types
-  MemoryBlock* = object
-    vk: VkDeviceMemory
-    size: uint64
-    rawPointer: pointer # if not nil, this is mapped memory
-    offsetNextFree: uint64
   BufferType* = enum
     VertexBuffer
     VertexBufferMapped
@@ -126,6 +117,11 @@
     IndexBufferMapped
     UniformBuffer
     UniformBufferMapped
+  MemoryBlock* = object
+    vk: VkDeviceMemory
+    size: uint64
+    rawPointer: pointer # if not nil, this is mapped memory
+    offsetNextFree: uint64
   Buffer* = object
     vk: VkBuffer
     size: uint64
@@ -151,46 +147,49 @@
     imageViews: seq[VkImageView]
     samplers: seq[VkSampler]
 
-template forDescriptorFields(shader: typed, fieldname, valuename, typename, countname, bindingNumber, body: untyped): untyped =
+var vulkan* = VulkanGlobals()
+var fullscreen_internal: bool
+
+proc `=copy`(dest: var VulkanGlobals; source: VulkanGlobals) {.error.}
+proc `=copy`(dest: var RenderData; source: RenderData) {.error.}
+proc `=copy`[T, S](dest: var GPUValue[T, S]; source: GPUValue[T, S]) {.error.}
+proc `=copy`[T, S](dest: var GPUArray[T, S]; source: GPUArray[T, S]) {.error.}
+proc `=copy`(dest: var MemoryBlock; source: MemoryBlock) {.error.}
+proc `=copy`[T](dest: var Pipeline[T]; source: Pipeline[T]) {.error.}
+proc `=copy`[T](dest: var DescriptorSet[T]; source: DescriptorSet[T]) {.error.}
+
+template forDescriptorFields(shader: typed, valuename, typename, countname, bindingNumber, body: untyped): untyped =
   var `bindingNumber` {.inject.} = 0'u32
-  for theFieldname, value in fieldPairs(shader):
-    when typeof(value) is Image:
+  for theFieldname, `valuename` in fieldPairs(shader):
+    when typeof(`valuename`) is Image:
       block:
-        const `fieldname` {.inject, hint[XDeclaredButNotUsed]: off.} = theFieldname
         const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
         const `countname` {.inject.} = 1'u32
-        let `valuename` {.inject, hint[XDeclaredButNotUsed]: off.} = value
         body
         `bindingNumber`.inc
-    elif typeof(value) is GPUValue:
+    elif typeof(`valuename`) is GPUValue:
       block:
-        const `fieldname` {.inject, hint[XDeclaredButNotUsed]: off.} = theFieldname
         const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
         const `countname` {.inject.} = 1'u32
-        let `valuename` {.inject, hint[XDeclaredButNotUsed]: off.} = value
         body
         `bindingNumber`.inc
-    elif typeof(value) is array:
-      when elementType(value) is Image:
+    elif typeof(`valuename`) is array:
+      when elementType(`valuename`) is Image:
         block:
-          const `fieldname` {.inject, hint[XDeclaredButNotUsed]: off.} = theFieldname
           const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
-          const `countname` {.inject.} = uint32(typeof(value).len)
-          let `valuename` {.inject, hint[XDeclaredButNotUsed]: off.} = value
+          const `countname` {.inject.} = uint32(typeof(`valuename`).len)
           body
           `bindingNumber`.inc
-      elif elementType(value) is GPUValue:
+      elif elementType(`valuename`) is GPUValue:
         block:
-          const `fieldname` {.inject, hint[XDeclaredButNotUsed]: off.} = theFieldname
           const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
-          const `countname` {.inject.} = len(value).uint32
-          let `valuename` {.inject hint[XDeclaredButNotUsed]: off.} = value
+          const `countname` {.inject.} = len(`valuename`).uint32
           body
           `bindingNumber`.inc
       else:
-        {.error: "Unsupported descriptor type: " & typetraits.name(typeof(value)).}
+        {.error: "Unsupported descriptor type: " & typetraits.name(typeof(`valuename`)).}
     else:
-      {.error: "Unsupported descriptor type: " & typetraits.name(typeof(value)).}
+      {.error: "Unsupported descriptor type: " & typetraits.name(typeof(`valuename`)).}
 
 include ./rendering/vulkan_wrappers
 include ./rendering/renderpasses
--- a/semicongine/rendering/renderer.nim	Wed Aug 14 20:06:51 2024 +0700
+++ b/semicongine/rendering/renderer.nim	Thu Aug 15 12:12:27 2024 +0700
@@ -86,12 +86,12 @@
       assert value.sampler.Valid
     elif typeof(value) is array:
       when elementType(value) is Image:
-        for t in value:
+        for t in value.litems:
           assert t.vk.Valid
           assert t.imageview.Valid
           assert t.sampler.Valid
       elif elementType(value) is GPUValue:
-        for t in value:
+        for t in value.litems:
           assert t.buffer.vk.Valid
       else:
         {.error: "Unsupported descriptor set field: '" & theName & "'".}
@@ -114,7 +114,7 @@
   var imageWrites = newSeqOfCap[VkDescriptorImageInfo](1024)
   var bufferWrites = newSeqOfCap[VkDescriptorBufferInfo](1024)
 
-  forDescriptorFields(descriptorSet.data, fieldName, fieldValue, descriptorType, descriptorCount, descriptorBindingNumber):
+  forDescriptorFields(descriptorSet.data, fieldValue, descriptorType, descriptorCount, descriptorBindingNumber):
     for i in 0 ..< descriptorSet.vk.len:
       when typeof(fieldValue) is GPUValue:
         bufferWrites.add VkDescriptorBufferInfo(
@@ -150,7 +150,7 @@
         )
       elif typeof(fieldValue) is array:
         when elementType(fieldValue) is Image:
-          for image in fieldValue:
+          for image in fieldValue.litems:
             imageWrites.add VkDescriptorImageInfo(
               sampler: image.sampler,
               imageView: image.imageView,
@@ -167,7 +167,7 @@
             pBufferInfo: nil,
           )
         elif elementType(fieldValue) is GPUValue:
-          for entry in fieldValue:
+          for entry in fieldValue.litems:
             bufferWrites.add VkDescriptorBufferInfo(
               buffer: entry.buffer.vk,
               offset: entry.offset,
@@ -224,7 +224,7 @@
 
 proc flushAllMemory*(renderData: RenderData) =
   var flushRegions = newSeq[VkMappedMemoryRange]()
-  for memoryBlocks in renderData.memory:
+  for memoryBlocks in renderData.memory.litems:
     for memoryBlock in memoryBlocks:
       if memoryBlock.rawPointer != nil and memoryBlock.offsetNextFree > 0:
         flushRegions.add VkMappedMemoryRange(
@@ -248,8 +248,7 @@
   # check if there is an existing allocated memory block that is large enough to be used
   var selectedBlockI = -1
   for i in 0 ..< renderData.memory[memoryType].len:
-    let memoryBlock = renderData.memory[memoryType][i]
-    if memoryBlock.size - alignedTo(memoryBlock.offsetNextFree, memoryRequirements.alignment) >= memoryRequirements.size:
+    if renderData.memory[memoryType][i].size - alignedTo(renderData.memory[memoryType][i].offsetNextFree, memoryRequirements.alignment) >= memoryRequirements.size:
       selectedBlockI = i
       break
   # otherwise, allocate a new block of memory and use that
@@ -260,7 +259,8 @@
       mType = memoryType
     )
 
-  let selectedBlock = renderData.memory[memoryType][selectedBlockI]
+  template selectedBlock(): untyped = renderData.memory[memoryType][selectedBlockI]
+  # let selectedBlock = 
   renderData.memory[memoryType][selectedBlockI].offsetNextFree = alignedTo(
     selectedBlock.offsetNextFree,
     memoryRequirements.alignment,
@@ -294,7 +294,7 @@
       updateGPUBuffer(fieldvalue, flush = flush)
     when typeof(fieldvalue) is array:
       when elementType(fieldvalue) is GPUData:
-        for entry in fieldvalue:
+        for entry in fieldvalue.litems:
           updateGPUBuffer(entry, flush = flush)
 
 proc allocateGPUData(
@@ -380,7 +380,7 @@
   for image in renderData.images:
     vkDestroyImage(vulkan.device, image, nil)
 
-  for memoryBlocks in renderData.memory:
+  for memoryBlocks in renderData.memory.litems:
     for memory in memoryBlocks:
       vkFreeMemory(vulkan.device, memory.vk, nil)
 
@@ -480,8 +480,7 @@
   # check if there is an existing allocated memory block that is large enough to be used
   var selectedBlockI = -1
   for i in 0 ..< renderData.memory[memoryType].len:
-    let memoryBlock = renderData.memory[memoryType][i]
-    if memoryBlock.size - alignedTo(memoryBlock.offsetNextFree, memoryRequirements.alignment) >= memoryRequirements.size:
+    if renderData.memory[memoryType][i].size - alignedTo(renderData.memory[memoryType][i].offsetNextFree, memoryRequirements.alignment) >= memoryRequirements.size:
       selectedBlockI = i
       break
   # otherwise, allocate a new block of memory and use that
@@ -491,7 +490,7 @@
       size = max(memoryRequirements.size, MEMORY_BLOCK_ALLOCATION_SIZE),
       mType = memoryType
     )
-  let selectedBlock = renderData.memory[memoryType][selectedBlockI]
+  template selectedBlock(): untyped = renderData.memory[memoryType][selectedBlockI]
   renderData.memory[memoryType][selectedBlockI].offsetNextFree = alignedTo(
     selectedBlock.offsetNextFree,
     memoryRequirements.alignment,
@@ -763,11 +762,11 @@
   );
   render(commandBuffer, pipeline, mesh, EMPTY())
 
-proc asGPUArray*[T](data: openArray[T], bufferType: static BufferType): auto =
+proc asGPUArray*[T](data: sink openArray[T], bufferType: static BufferType): auto =
   GPUArray[T, bufferType](data: @data)
 
-proc asGPUValue*[T](data: T, bufferType: static BufferType): auto =
+proc asGPUValue*[T](data: sink T, bufferType: static BufferType): auto =
   GPUValue[T, bufferType](data: data)
 
-proc asDescriptorSet*[T](data: T): auto =
+proc asDescriptorSet*[T](data: sink T): auto =
   DescriptorSet[T](data: data)
--- a/semicongine/rendering/shaders.nim	Wed Aug 14 20:06:51 2024 +0700
+++ b/semicongine/rendering/shaders.nim	Thu Aug 15 12:12:27 2024 +0700
@@ -338,7 +338,7 @@
     when hasCustomPragma(value, DescriptorSets):
       for descriptorSet in value.fields:
         var layoutbindings: seq[VkDescriptorSetLayoutBinding]
-        forDescriptorFields(descriptorSet, fieldName, fieldValue, descriptorType, descriptorCount, descriptorBindingNumber):
+        forDescriptorFields(descriptorSet, fieldValue, descriptorType, descriptorCount, descriptorBindingNumber):
           layoutbindings.add VkDescriptorSetLayoutBinding(
             binding: descriptorBindingNumber,
             descriptorType: descriptorType,
--- a/semicongine/text/textbox.nim	Wed Aug 14 20:06:51 2024 +0700
+++ b/semicongine/text/textbox.nim	Thu Aug 15 12:12:27 2024 +0700
@@ -19,6 +19,8 @@
     indices: GPUArray[uint16, IndexBuffer]
     shaderdata: DescriptorSet[TextboxDescriptorSet]
 
+proc `=copy`(dest: var Textbox; source: Textbox) {.error.}
+
 func `$`*(textbox: Textbox): string =
   "\"" & $textbox.text[0 ..< min(textbox.text.len, 16)] & "\""
 
--- a/tests/test_gltf.nim	Wed Aug 14 20:06:51 2024 +0700
+++ b/tests/test_gltf.nim	Thu Aug 15 12:12:27 2024 +0700
@@ -136,7 +136,7 @@
   proc drawNode(commandbuffer: VkCommandBuffer, pipeline: Pipeline, nodeId: int, transform: Mat4) =
     let nodeTransform = gltfData.nodes[nodeId].transform * transform
     if gltfData.nodes[nodeId].mesh >= 0:
-      for primitive in gltfData.meshes[gltfData.nodes[nodeId].mesh]:
+      for primitive in gltfData.meshes[gltfData.nodes[nodeId].mesh].mitems:
         renderWithPushConstant(
           commandbuffer = commandbuffer,
           pipeline = pipeline,
--- a/tests/test_text.nim	Wed Aug 14 20:06:51 2024 +0700
+++ b/tests/test_text.nim	Thu Aug 15 12:12:27 2024 +0700
@@ -91,7 +91,7 @@
     withNextFrame(framebuffer, commandbuffer):
       withRenderPass(vulkan.swapchain.renderPass, framebuffer, commandbuffer, vulkan.swapchain.width, vulkan.swapchain.height, vec4(0, 0, 0, 0)):
         withPipeline(commandbuffer, pipeline):
-          for label in labels:
+          for label in labels.litems:
             render(commandbuffer, pipeline, label)
 
       # cleanup
@@ -177,7 +177,7 @@
       scale = rand(0.0002 .. 0.002),
       position = vec3(rand(-0.5 .. 0.5), rand(-0.5 .. 0.5), rand(-0.1 .. 0.1))
     )
-  labels = labels.sortedByIt(-it.position.z)
+  labels.sort(proc(x, y: Textbox): int = cmp(x.position.z, y.position.z), Ascending)
 
   var start = getMonoTime()
   while ((getMonoTime() - start).inMilliseconds().int / 1000) < time: