# HG changeset patch # User sam # Date 1723698747 -25200 # Node ID 385dbd68a947decfe8a29da1da378fda89c7cbd9 # Parent 19e3eedb9a41e4925a2552b585a0c755d46080de did: a TON of copy elimination, some tests run now waaaay faster diff -r 19e3eedb9a41 -r 385dbd68a947 semicongine/audio/mixer.nim --- 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) = diff -r 19e3eedb9a41 -r 385dbd68a947 semicongine/core/utils.nim --- 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) diff -r 19e3eedb9a41 -r 385dbd68a947 semicongine/gltf.nim --- 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 diff -r 19e3eedb9a41 -r 385dbd68a947 semicongine/image.nim --- 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") diff -r 19e3eedb9a41 -r 385dbd68a947 semicongine/rendering.nim --- 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 diff -r 19e3eedb9a41 -r 385dbd68a947 semicongine/rendering/renderer.nim --- 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) diff -r 19e3eedb9a41 -r 385dbd68a947 semicongine/rendering/shaders.nim --- 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, diff -r 19e3eedb9a41 -r 385dbd68a947 semicongine/text/textbox.nim --- 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)] & "\"" diff -r 19e3eedb9a41 -r 385dbd68a947 tests/test_gltf.nim --- 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, diff -r 19e3eedb9a41 -r 385dbd68a947 tests/test_text.nim --- 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: