Mercurial > games > semicongine
changeset 1498:d3d667bbdda4 default tip
did: add support for per-frame-buffers, still need to limit this, to maybe only mapped buffers
author | sam <sam@basx.dev> |
---|---|
date | Thu, 25 Sep 2025 23:53:41 +0700 |
parents | 56aa8fe70e9e |
children | |
files | semicongine/core/types.nim semicongine/rendering/memory.nim semicongine/rendering/renderer.nim semicongine/rendering/renderpasses.nim tests/test_rendering.nim |
diffstat | 5 files changed, 94 insertions(+), 87 deletions(-) [+] |
line wrap: on
line diff
--- a/semicongine/core/types.nim Wed Sep 24 23:55:36 2025 +0700 +++ b/semicongine/core/types.nim Thu Sep 25 23:53:41 2025 +0700 @@ -140,13 +140,13 @@ # TODO: when using mapped buffer memory, directly write values to mapped location # instead of using data as buffer data*: seq[T] - buffer*: Buffer - offset*: uint64 + buffer*: array[INFLIGHTFRAMES.int, Buffer] + offset*: array[INFLIGHTFRAMES.int, uint64] GPUValue*[T: object, TBuffer: static BufferType] = object data*: T - buffer*: Buffer - offset*: uint64 + buffer*: array[INFLIGHTFRAMES.int, Buffer] + offset*: array[INFLIGHTFRAMES.int, uint64] GPUData* = GPUArray | GPUValue
--- a/semicongine/rendering/memory.nim Wed Sep 24 23:55:36 2025 +0700 +++ b/semicongine/rendering/memory.nim Thu Sep 25 23:53:41 2025 +0700 @@ -121,8 +121,8 @@ # santization checks for theName, value in descriptorSet.data[0].fieldPairs: when typeof(value) is GPUValue: - assert value.buffer.vk.Valid, - "Invalid buffer, did you call 'assignBuffers' for this buffer?" + for i in 0 ..< INFLIGHTFRAMES: + assert value.buffer[i].vk.Valid, "Invalid buffer, did you call 'assignBuffers' for this buffer?" elif typeof(value) is ImageObject: assert value.vk.Valid assert value.imageview.Valid @@ -135,7 +135,8 @@ assert t.sampler.Valid elif elementType(value) is GPUValue: for t in value.litems: - assert t.buffer.vk.Valid + for i in 0 ..< INFLIGHTFRAMES: + assert t.buffer[i].vk.Valid else: {.error: "Unsupported descriptor set field: '" & theName & "'".} else: @@ -166,8 +167,8 @@ const descriptorBindingNumber = getBindingNumber[typeof(descriptorSet.data[fI])](theFieldname) when typeof(fieldvalue) is GPUValue: bufferWrites.add VkDescriptorBufferInfo( - buffer: fieldvalue.buffer.vk, - offset: fieldvalue.offset, + buffer: fieldvalue.buffer[fi].vk, + offset: fieldvalue.offset[fi], range: fieldvalue.size, ) descriptorSetWrites.add VkWriteDescriptorSet( @@ -217,7 +218,7 @@ elif elementType(fieldvalue) is GPUValue: for entry in fieldvalue.litems: bufferWrites.add VkDescriptorBufferInfo( - buffer: entry.buffer.vk, offset: entry.offset, range: entry.size + buffer: entry.buffer[fI].vk, offset: entry.offset[fI], range: entry.size ) descriptorSetWrites.add VkWriteDescriptorSet( sType: VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, @@ -267,20 +268,23 @@ proc flushAllMemory*(renderData: RenderData) = var flushRegions = newSeq[VkMappedMemoryRange]() + # var mType = 0'u32; for memoryBlocks in renderData.memory.litems: - for memoryBlock in memoryBlocks: - if memoryBlock.rawPointer != nil and memoryBlock.offsetNextFree > 0: - flushRegions.add VkMappedMemoryRange( - sType: VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, - memory: memoryBlock.vk, - size: alignedTo( - memoryBlock.offsetNextFree, - svkGetPhysicalDeviceProperties().limits.nonCoherentAtomSize, - ), - ) + # if mType.isMappable() or true: + for memoryBlock in memoryBlocks: + if memoryBlock.rawPointer != nil and memoryBlock.offsetNextFree > 0: + flushRegions.add VkMappedMemoryRange( + sType: VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, + memory: memoryBlock.vk, + size: alignedTo( + memoryBlock.offsetNextFree, + svkGetPhysicalDeviceProperties().limits.nonCoherentAtomSize, + ), + ) + # mType.inc if flushRegions.len > 0: checkVkResult vkFlushMappedMemoryRanges( - engine().vulkan.device, flushRegions.len.uint32, flushRegions.ToCPointer() + engine().vulkan.device, flushRegions.len.uint32, addr flushRegions[0] ) proc allocateNewBuffer( @@ -317,7 +321,6 @@ template selectedBlock(): untyped = renderData.memory[memoryType][selectedBlockI] - # let selectedBlock = renderData.memory[memoryType][selectedBlockI].offsetNextFree = alignedTo(selectedBlock.offsetNextFree, memoryRequirements.alignment) checkVkResult vkBindBufferMemory( @@ -325,31 +328,30 @@ ) result.memory = selectedBlock.vk result.memoryOffset = selectedBlock.offsetNextFree - result.rawPointer = - selectedBlock.rawPointer.pointerAddOffset(selectedBlock.offsetNextFree) + result.rawPointer = selectedBlock.rawPointer.pointerAddOffset(selectedBlock.offsetNextFree) renderData.memory[memoryType][selectedBlockI].offsetNextFree += memoryRequirements.size -proc updateGPUBuffer*(gpuData: GPUData, count = 0'u64, flush = false) = +proc updateGPUBuffer*(gpuData: GPUData, frame: int, count = 0'u64, flush = false) = if gpuData.size() == 0: return when needsMapping(gpuData): when gpuData is GPUArray: copyMem( - pointerAddOffset(gpuData.buffer.rawPointer, gpuData.offset), + pointerAddOffset(gpuData.buffer[currentFiF()].rawPointer, gpuData.offset[currentFiF()]), gpuData.rawPointer, gpuData.size(count), ) else: copyMem( - pointerAddOffset(gpuData.buffer.rawPointer, gpuData.offset), + pointerAddOffset(gpuData.buffer[currentFiF()].rawPointer, gpuData.offset[currentFiF()]), gpuData.rawPointer, gpuData.size(), ) if flush: - flushBuffer(gpuData.buffer) + flushBuffer(gpuData.buffer[frame]) else: - withStagingBuffer((gpuData.buffer.vk, gpuData.offset), gpuData.size, stagingPtr): + withStagingBuffer((gpuData.buffer[frame].vk, gpuData.offset[frame]), gpuData.size, stagingPtr): when gpuData is GPUArray: copyMem(stagingPtr, gpuData.rawPointer, gpuData.size(count)) else: @@ -358,11 +360,13 @@ proc updateAllGPUBuffers*[T](value: T, flush = false) = for name, fieldvalue in value.fieldPairs(): when typeof(fieldvalue) is GPUData: - updateGPUBuffer(fieldvalue, flush = flush) + for i in 0 ..< INFLIGHTFRAMES.int: + updateGPUBuffer(fieldvalue, frame=i, flush = flush) when typeof(fieldvalue) is array: when elementType(fieldvalue) is GPUData: for entry in fieldvalue.litems: - updateGPUBuffer(entry, flush = flush) + for i in 0 ..< INFLIGHTFRAMES.int: + updateGPUBuffer(entry, frame=i, flush = flush) proc allocateGPUData( renderdata: var RenderData, bufferType: BufferType, size: uint64 @@ -394,15 +398,16 @@ proc assignBuffers*[T](renderdata: var RenderData, data: var T, uploadData = true) = for name, value in fieldPairs(data): when typeof(value) is GPUData: - (value.buffer, value.offset) = - allocateGPUData(renderdata, value.bufferType, value.size) + for i in 0 ..< INFLIGHTFRAMES.int: + (value.buffer[i], value.offset[i]) = allocateGPUData(renderdata, value.bufferType, value.size) elif typeof(value) is DescriptorSetData: - for i in 0 ..< INFLIGHTFRAMES: + for i in 0 ..< INFLIGHTFRAMES.int: assignBuffers(renderdata, value.data[i], uploadData = uploadData) elif typeof(value) is array: when elementType(value) is GPUValue: for v in value.mitems: - (v.buffer, v.offset) = allocateGPUData(renderdata, v.bufferType, v.size) + for i in 0 ..< INFLIGHTFRAMES.int: + (v.buffer[i], v.offset[i]) = allocateGPUData(renderdata, v.bufferType, v.size) if uploadData: updateAllGPUBuffers(data, flush = true)
--- a/semicongine/rendering/renderer.nim Wed Sep 24 23:55:36 2025 +0700 +++ b/semicongine/rendering/renderer.nim Thu Sep 25 23:53:41 2025 +0700 @@ -218,8 +218,7 @@ index: static DescriptorSetIndex, layout: VkPipelineLayout, ) = - assert descriptorSet.vk[currentFiF()].Valid, - "DescriptorSetData not initialized, maybe forgot to call initDescriptorSet" + assert descriptorSet.vk[currentFiF()].Valid, "DescriptorSetData not initialized, maybe forgot to call initDescriptorSet" svkCmdBindDescriptorSet(commandBuffer, descriptorSet.vk[currentFiF()], index, layout) proc bindDescriptorSet*[TDescriptorSet, TShader]( @@ -286,10 +285,10 @@ for meshName, meshValue in mesh.fieldPairs: when meshName == shaderAttributeName: debug(" vertex attr: ", shaderAttributeName) - assert meshValue.buffer.vk.Valid, + assert meshValue.buffer[currentFiF()].vk.Valid, "Mesh vertex-attribute '{TMesh}.{shaderAttributeName}' has no valid buffer (encountered while rendering with '{TShader}')" - vertexBuffers.add meshValue.buffer.vk - vertexBuffersOffsets.add meshValue.offset + vertexBuffers.add meshValue.buffer[currentFiF()].vk + vertexBuffersOffsets.add meshValue.offset[currentFiF()] if elementCount == 0: elementCount = meshValue.data.len.uint32 else: @@ -301,8 +300,8 @@ for instanceName, instanceValue in instances.fieldPairs: when instanceName == shaderAttributeName: debug(" instnc attr: ", shaderAttributeName) - vertexBuffers.add instanceValue.buffer.vk - vertexBuffersOffsets.add instanceValue.offset + vertexBuffers.add instanceValue.buffer[currentFiF()].vk + vertexBuffersOffsets.add instanceValue.offset[currentFiF()] if instanceCount == 1: instanceCount = instanceValue.data.len.uint32 else: @@ -326,18 +325,18 @@ for meshName, meshValue in mesh.fieldPairs: when typeof(meshValue) is GPUArray[uint8, IndexBuffer]: - indexBuffer = meshValue.buffer.vk - indexBufferOffset = meshValue.offset + indexBuffer = meshValue.buffer[currentFiF()].vk + indexBufferOffset = meshValue.offset[currentFiF()] indexType = VK_INDEX_TYPE_UINT8_EXT elementCount = meshValue.data.len.uint32 elif typeof(meshValue) is GPUArray[uint16, IndexBuffer]: - indexBuffer = meshValue.buffer.vk - indexBufferOffset = meshValue.offset + indexBuffer = meshValue.buffer[currentFiF()].vk + indexBufferOffset = meshValue.offset[currentFiF()] indexType = VK_INDEX_TYPE_UINT16 elementCount = meshValue.data.len.uint32 elif typeof(meshValue) is GPUArray[uint32, IndexBuffer]: - indexBuffer = meshValue.buffer.vk - indexBufferOffset = meshValue.offset + indexBuffer = meshValue.buffer[currentFiF()].vk + indexBufferOffset = meshValue.offset[currentFiF()] indexType = VK_INDEX_TYPE_UINT32 elementCount = meshValue.data.len.uint32 @@ -435,4 +434,6 @@ render(commandBuffer, pipeline, mesh, EMPTY(), fixedVertexCount, fixedInstanceCount) proc asDescriptorSetData*[T](data: sink T): auto = - DescriptorSetData[T](data: data) + var data1: T + copyMem(addr(data1), addr(data), sizeof(T)) + DescriptorSetData[T](data: [data, data1])
--- a/semicongine/rendering/renderpasses.nim Wed Sep 24 23:55:36 2025 +0700 +++ b/semicongine/rendering/renderpasses.nim Thu Sep 25 23:53:41 2025 +0700 @@ -4,7 +4,9 @@ proc createDirectPresentationRenderPass*( depthBuffer: bool, samples = VK_SAMPLE_COUNT_1_BIT ): RenderPass = - result = RenderPass(depthBuffer: depthBuffer, samples: samples) + result = new(RenderPass) + result.depthBuffer = depthBuffer + result.samples = samples var attachments = @[
--- a/tests/test_rendering.nim Wed Sep 24 23:55:36 2025 +0700 +++ b/tests/test_rendering.nim Thu Sep 25 23:53:41 2025 +0700 @@ -10,6 +10,7 @@ import ../semicongine/rendering import ../semicongine/input import ../semicongine/loaders +import ../semicongine/images proc test_01_triangle(time: float32, renderPass: RenderPass) = var renderdata = initRenderData() @@ -374,14 +375,12 @@ ) var otherset1 = asDescriptorSetData( OtherSet( - objectSettings: - asGPUValue(ObjectSettings(scale: 1.0, materialIndex: 0), UniformBufferMapped) + objectSettings: asGPUValue(ObjectSettings(scale: 1.0, materialIndex: 0), UniformBufferMapped) ) ) var otherset2 = asDescriptorSetData( OtherSet( - objectSettings: - asGPUValue(ObjectSettings(scale: 1.0, materialIndex: 1), UniformBufferMapped) + objectSettings: asGPUValue(ObjectSettings(scale: 1.0, materialIndex: 1), UniformBufferMapped) ) ) @@ -400,8 +399,19 @@ initDescriptorSet(renderdata, pipeline.descriptorSetLayouts[2], otherset1) initDescriptorSet(renderdata, pipeline.descriptorSetLayouts[2], otherset2) + updateGPUBuffer(otherset2.data[0].objectSettings, frame=0) + updateGPUBuffer(otherset2.data[1].objectSettings, frame=1) + var start = getMonoTime() while ((getMonoTime() - start).inMilliseconds().int / 1000) < time: + + mainset.data[currentFiF()].renderSettings.data.brigthness = ((getMonoTime() - start).inMilliseconds().int / 1000) / time + otherset1.data[currentFiF()].objectSettings.data.scale = 0.5 + ((getMonoTime() - start).inMilliseconds().int / 1000) / time + updateGPUBuffer(mainset.data[currentFiF()].renderSettings, frame=currentFiF()) + updateGPUBuffer(otherset1.data[currentFiF()].objectSettings, frame=currentFiF()) + updateGPUBuffer(otherset2.data[currentFiF()].objectSettings, frame=currentFiF()) + renderdata.flushAllMemory() + withNextFrame(framebuffer, commandbuffer): bindDescriptorSet(commandbuffer, constset, 0, pipeline) bindDescriptorSet(commandbuffer, mainset, 1, pipeline) @@ -421,14 +431,6 @@ bindDescriptorSet(commandbuffer, otherset2, 2, pipeline) render(commandbuffer = commandbuffer, pipeline = pipeline, mesh = quad) - mainset.data.renderSettings.data.brigthness = - ((getMonoTime() - start).inMilliseconds().int / 1000) / time - otherset1.data.objectSettings.data.scale = - 0.5 + ((getMonoTime() - start).inMilliseconds().int / 1000) / time - updateGPUBuffer(mainset.data.renderSettings) - updateGPUBuffer(otherset1.data.objectSettings) - renderdata.flushAllMemory() - # cleanup checkVkResult vkDeviceWaitIdle(engine().vulkan.device) destroyPipeline(pipeline) @@ -548,13 +550,13 @@ while ((getMonoTime() - start).inMilliseconds().int / 1000) < time: let tStartLoop = getMonoTime() - tStart - uniforms1.data.data.data.mvp = ( + uniforms1.data[currentFiF()].data.data.mvp = ( projection(-PI / 2, getAspectRatio(), 0.01, 100) * translate(0, 0, 2) * rotate(PI / 4, X) * rotate( PI * 0.1 * (tStartLoop.inMicroseconds() / 1_000_000), Y ) ) - updateGPUBuffer(uniforms1.data.data, flush = true) + updateGPUBuffer(uniforms1.data[currentFiF()].data, frame=currentFiF(), flush = true) withNextFrame(framebuffer, commandbuffer): withRenderPass( @@ -831,7 +833,7 @@ var (offscreenRP, presentRP) = createIndirectPresentationRenderPass(depthBuffer = depthBuffer, samples = samples) - setupSwapchain(renderpass = presentRP) + setupSwapchain(renderpass = presentRP, vSync = false, tripleBuffering = false) var renderdata = initRenderData() @@ -886,19 +888,16 @@ indices: GPUArray[uint16, IndexBuffer] var mesh = TriangleMesh( - position: - asGPUArray([vec3(-0.5, -0.5), vec3(0, 0.5), vec3(0.5, -0.5)], VertexBuffer), - color: asGPUArray([vec3(0, 0, 1), vec3(0, 1, 0), vec3(1, 0, 0)], VertexBuffer), + position: asGPUArray([vec3(-0.5, -0.5), vec3(0, 0.5), vec3(0.5, -0.5)], VertexBuffer), + color: asGPUArray([vec3(0, 0, 1), vec3(0, 1, 0), vec3(1, 0, 0)], VertexBuffer), ) var quad = QuadMesh( - position: - asGPUArray([vec2(-1, -1), vec2(-1, 1), vec2(1, 1), vec2(1, -1)], VertexBuffer), - indices: asGPUArray([0'u16, 1'u16, 2'u16, 2'u16, 3'u16, 0'u16], IndexBuffer), + position: asGPUArray([vec2(-1, -1), vec2(-1, 1), vec2(1, 1), vec2(1, -1)], VertexBuffer), + indices: asGPUArray([0'u16, 1'u16, 2'u16, 2'u16, 3'u16, 0'u16], IndexBuffer), ) var uniforms1 = asDescriptorSetData( Uniforms( - frameTexture: - Image[BGRA](width: frameWidth(), height: frameHeight(), isRenderTarget: true) + frameTexture: Image[BGRA](width: frameWidth(), height: frameHeight(), isRenderTarget: true) ) ) assignBuffers(renderdata, mesh) @@ -934,7 +933,7 @@ image = depthImage, format = DEPTH_FORMAT, aspect = VK_IMAGE_ASPECT_DEPTH_BIT ) - # create msaa images (will not use the one in the swapchain + # create msaa images (will not use the one in the swapchain) var msaaImage: VkImage msaaImageView: VkImageView @@ -948,26 +947,24 @@ samples = offscreenRP.samples, ) let requirements = svkGetImageMemoryRequirements(msaaImage) - msaaMemory = svkAllocateMemory( - requirements.size, bestMemory(mappable = false, filter = requirements.memoryTypes) - ) + msaaMemory = svkAllocateMemory(requirements.size, bestMemory(mappable = false, filter = requirements.memoryTypes)) checkVkResult vkBindImageMemory(engine().vulkan.device, msaaImage, msaaMemory, 0) msaaImageView = svkCreate2DImageView(image = msaaImage, format = SURFACE_FORMAT) var attachments: seq[VkImageView] if offscreenRP.samples == VK_SAMPLE_COUNT_1_BIT: if offscreenRP.depthBuffer: - attachments = @[uniforms1.data.frameTexture.imageview, depthImageView] + attachments = @[uniforms1.data[currentFiF()].frameTexture.imageview, depthImageView] else: - attachments = @[uniforms1.data.frameTexture.imageview] + attachments = @[uniforms1.data[currentFiF()].frameTexture.imageview] else: if offscreenRP.depthBuffer: - attachments = - @[msaaImageView, depthImageView, uniforms1.data.frameTexture.imageview] + attachments = @[msaaImageView, depthImageView, uniforms1.data[currentFiF()].frameTexture.imageview] else: - attachments = @[msaaImageView, uniforms1.data.frameTexture.imageview] - var offscreenFB = - svkCreateFramebuffer(offscreenRP.vk, frameWidth(), frameHeight(), attachments) + attachments = @[msaaImageView, uniforms1.data[currentFiF()].frameTexture.imageview] + var offscreenFB = svkCreateFramebuffer(offscreenRP.vk, frameWidth(), frameHeight(), attachments) + + renderdata.flushAllMemory() var start = getMonoTime() while ((getMonoTime() - start).inMilliseconds().int / 1000) < time: @@ -1024,11 +1021,12 @@ (depthBuffer: true, samples: VK_SAMPLE_COUNT_4_BIT), ] + #[ # test normal + var renderpass: RenderPass for i, (depthBuffer, samples) in renderPasses: - var renderpass = - createDirectPresentationRenderPass(depthBuffer = depthBuffer, samples = samples) - setupSwapchain(renderpass = renderpass) + renderpass = createDirectPresentationRenderPass(depthBuffer = depthBuffer, samples = samples) + setupSwapchain(renderpass = renderpass, vSync = false, tripleBuffering = false) # tests a simple triangle with minimalistic shader and vertex format test_01_triangle(time, renderpass) @@ -1055,6 +1053,7 @@ checkVkResult vkDeviceWaitIdle(engine().vulkan.device) destroyRenderPass(renderpass) clearSwapchain() + ]# # test multiple render passes for i, (depthBuffer, samples) in renderPasses: