Mercurial > games > semicongine
changeset 142:9c0d7839dc91
add: correct mesh buffer data updates to GPU
author | Sam <sam@basx.dev> |
---|---|
date | Tue, 25 Apr 2023 18:23:57 +0700 |
parents | 8bb27869b649 |
children | 8ce634aa6ea6 |
files | src/semicongine/engine.nim src/semicongine/entity.nim src/semicongine/mesh.nim src/semicongine/renderer.nim src/semicongine/vulkan/renderpass.nim src/semicongine/vulkan/shader.nim |
diffstat | 6 files changed, 105 insertions(+), 21 deletions(-) [+] |
line wrap: on
line diff
--- a/src/semicongine/engine.nim Sat Apr 22 17:34:59 2023 +0700 +++ b/src/semicongine/engine.nim Tue Apr 25 18:23:57 2023 +0700 @@ -1,3 +1,4 @@ +import std/sequtils import ./platform/window import ./vulkan/api @@ -9,6 +10,7 @@ import ./gpu_data import ./entity import ./renderer +import ./mesh import ./events import ./config import ./math @@ -86,13 +88,15 @@ proc setRenderer*(engine: var Engine, renderPass: RenderPass) = engine.renderer = engine.device.initRenderer(renderPass) -proc addScene*(engine: var Engine, entity: Entity, vertexInput: seq[ShaderAttribute]) = - engine.renderer.setupDrawableBuffers(entity, vertexInput) +proc addScene*(engine: var Engine, scene: Entity, vertexInput: seq[ShaderAttribute], transformAttribute="") = + assert transformAttribute in map(vertexInput, proc(a: ShaderAttribute): string = a.name) + engine.renderer.setupDrawableBuffers(scene, vertexInput, transformAttribute=transformAttribute) -proc renderScene*(engine: var Engine, entity: Entity) = +proc renderScene*(engine: var Engine, scene: Entity) = assert engine.renderer.valid if engine.running: - engine.renderer.render(entity) + engine.renderer.refreshMeshData(scene) + engine.renderer.render(scene) proc updateInputs*(engine: var Engine) = if not engine.running: @@ -141,7 +145,7 @@ func mouseIsDown*(engine: Engine, button: MouseButton): auto = button in engine.input.mouseIsDown func mouseWasPressed*(engine: Engine, button: MouseButton): auto = button in engine.input.mouseWasPressed func mouseWasReleased*(engine: Engine, button: MouseButton): auto = button in engine.input.mouseWasReleased -func mousePosition*(engine: Engine, key: Key): auto = engine.input.mousePosition +func mousePosition*(engine: Engine): auto = engine.input.mousePosition func eventsProcessed*(engine: Engine): auto = engine.input.eventsProcessed func framesRendered*(engine: Engine): auto = engine.renderer.framesRendered func gpuDevice*(engine: Engine): Device = engine.device
--- a/src/semicongine/entity.nim Sat Apr 22 17:34:59 2023 +0700 +++ b/src/semicongine/entity.nim Tue Apr 25 18:23:57 2023 +0700 @@ -31,6 +31,9 @@ func hash*(entity: Entity): Hash = hash(cast[pointer](entity)) +func hash*(component: Component): Hash = + hash(cast[pointer](component)) + method `$`*(entity: Entity): string {.base.} = entity.name method `$`*(component: Component): string {.base.} = "Unknown Component"
--- a/src/semicongine/mesh.nim Sat Apr 22 17:34:59 2023 +0700 +++ b/src/semicongine/mesh.nim Tue Apr 25 18:23:57 2023 +0700 @@ -22,6 +22,7 @@ indicesCount*: uint32 instanceCount*: uint32 data: Table[string, DataList] + changedAttributes: seq[string] case indexType*: MeshIndexType of None: discard of Tiny: tinyIndices: seq[array[3, uint8]] @@ -86,6 +87,9 @@ ): auto = newMesh(positions, newSeq[array[3, int]](), colors, instanceCount) +func availableAttributes*(mesh: Mesh): seq[string] = + mesh.data.keys.toSeq + func dataSize*(mesh: Mesh, attribute: string): uint32 = mesh.data[attribute].size @@ -112,6 +116,10 @@ func getRawData*(mesh: Mesh, attribute: string): (pointer, uint32) = mesh.data[attribute].getRawData() +proc getMeshData*[T: GPUType|int|uint|float](mesh: Mesh, attribute: string): seq[T] = + assert attribute in mesh.data + get[T](mesh.data[attribute]) + proc setMeshData*[T: GPUType|int|uint|float](mesh: var Mesh, attribute: string, data: seq[T]) = assert not (attribute in mesh.data) mesh.data[attribute] = DataList(thetype: getDataType[T]()) @@ -123,6 +131,22 @@ mesh.data[attribute] = DataList(thetype: getDataType[T]()) setValues(mesh.data[attribute], data) +proc updateMeshData*[T: GPUType|int|uint|float](mesh: var Mesh, attribute: string, i: uint32, value: T) = + assert attribute in mesh.data + mesh.changedAttributes.add attribute + setValue(mesh.data[attribute], i, value) + +proc updateInstanceData*[T: GPUType|int|uint|float](mesh: var Mesh, attribute: string, data: seq[T]) = + assert uint32(data.len) == mesh.instanceCount + assert attribute in mesh.data + mesh.changedAttributes.add attribute + setValues(mesh.data[attribute], data) + +func hasDataChanged*(mesh: Mesh, attribute: string): bool = + attribute in mesh.changedAttributes + +proc clearDataChanged*(mesh: var Mesh) = + mesh.changedAttributes = @[] func rect*(width=1'f32, height=1'f32, color="ffffff"): Mesh = result = new Mesh
--- a/src/semicongine/renderer.nim Sat Apr 22 17:34:59 2023 +0700 +++ b/src/semicongine/renderer.nim Tue Apr 25 18:23:57 2023 +0700 @@ -15,12 +15,17 @@ import ./entity import ./mesh import ./gpu_data +import ./math type SceneData = object - drawables*: seq[Drawable] + drawables*: OrderedTable[Mesh, Drawable] vertexBuffers*: Table[MemoryLocation, Buffer] indexBuffer*: Buffer + attributeLocation*: Table[string, MemoryLocation] + attributeBindingNumber*: Table[string, int] + transformAttribute: string # name of attribute that is used for per-instance mesh transformation + entityTransformationCache: Table[Mesh, Mat4] # remembers last transformation, avoid to send GPU-updates if no changes Renderer* = object device: Device surfaceFormat: VkSurfaceFormatKHR @@ -42,12 +47,23 @@ raise newException(Exception, "Unable to create swapchain") result.swapchain = swapchain.get() -proc setupDrawableBuffers*(renderer: var Renderer, tree: Entity, inputs: seq[ShaderAttribute]) = - assert not (tree in renderer.scenedata) +proc setupDrawableBuffers*(renderer: var Renderer, scene: Entity, inputs: seq[ShaderAttribute], transformAttribute="") = + assert not (scene in renderer.scenedata) var data = SceneData() + if transformattribute != "": + var hasTransformAttribute = false + for input in inputs: + if input.name == transformattribute: + assert input.perInstance == true, $input + assert getDataType[Mat4]() == input.thetype + hasTransformAttribute = true + assert hasTransformAttribute + data.transformAttribute = transformAttribute + + var allMeshes: seq[Mesh] - for mesh in allComponentsOfType[Mesh](tree): + for mesh in allComponentsOfType[Mesh](scene): allMeshes.add mesh for inputAttr in inputs: assert mesh.hasDataFor(inputAttr.name), &"{mesh} missing data for {inputAttr}" @@ -73,9 +89,14 @@ ) # one vertex data buffer per memory location - var perLocationOffsets: Table[MemoryLocation, uint64] - var perLocationSizes: Table[MemoryLocation, uint64] + var + perLocationOffsets: Table[MemoryLocation, uint64] + perLocationSizes: Table[MemoryLocation, uint64] + bindingNumber = 0 for attribute in inputs: + data.attributeLocation[attribute.name] = attribute.memoryLocation + data.attributeBindingNumber[attribute.name] = bindingNumber + inc bindingNumber # setup one buffer per attribute-location-type if not (attribute.memoryLocation in perLocationSizes): perLocationSizes[attribute.memoryLocation] = 0'u64 @@ -121,11 +142,41 @@ var (pdata, size) = mesh.getRawIndexData() data.indexBuffer.setData(pdata, size, indexBufferOffset) indexBufferOffset += size - data.drawables.add drawable + data.drawables[mesh] = drawable + + renderer.scenedata[scene] = data + +proc refreshMeshAttributeData(sceneData: var SceneData, mesh: Mesh, attribute: string) = + debug &"Refreshing data on mesh {mesh} for {attribute}" + var (pdata, size) = mesh.getRawData(attribute) + let memoryLocation = sceneData.attributeLocation[attribute] + let bindingNumber = sceneData.attributeBindingNumber[attribute] + sceneData.vertexBuffers[memoryLocation].setData(pdata, size, sceneData.drawables[mesh].bufferOffsets[bindingNumber][1]) + + +proc refreshMeshData*(renderer: var Renderer, scene: Entity) = + assert scene in renderer.scenedata - renderer.scenedata[tree] = data + for mesh in allComponentsOfType[Mesh](scene): + # if mesh transformation attribute is enabled, update the model matrix + if renderer.scenedata[scene].transformAttribute != "": + let transform = mesh.entity.getModelTransform() + if not (mesh in renderer.scenedata[scene].entityTransformationCache) or renderer.scenedata[scene].entityTransformationCache[mesh] != transform: + mesh.updateInstanceData(renderer.scenedata[scene].transformAttribute, @[transform]) + renderer.scenedata[scene].entityTransformationCache[mesh] = transform -proc render*(renderer: var Renderer, entity: Entity) = + # update any changed mesh attributes + for attribute in mesh.availableAttributes(): + if mesh.hasDataChanged(attribute): + renderer.scenedata[scene].refreshMeshAttributeData(mesh, attribute) + var m = mesh + m.clearDataChanged() + + + +proc render*(renderer: var Renderer, scene: Entity) = + assert scene in renderer.scenedata + var commandBufferResult = renderer.swapchain.nextFrame() commandBuffer: VkCommandBuffer @@ -150,15 +201,15 @@ var mpipeline = pipeline commandBuffer.vkCmdBindPipeline(subpass.pipelineBindPoint, mpipeline.vk) commandBuffer.vkCmdBindDescriptorSets(subpass.pipelineBindPoint, mpipeline.layout, 0, 1, addr(mpipeline.descriptorSets[renderer.swapchain.currentInFlight].vk), 0, nil) - mpipeline.updateUniforms(entity, renderer.swapchain.currentInFlight) + mpipeline.updateUniforms(scene, renderer.swapchain.currentInFlight) debug "Scene buffers:" - for (location, buffer) in renderer.scenedata[entity].vertexBuffers.pairs: + for (location, buffer) in renderer.scenedata[scene].vertexBuffers.pairs: debug " ", location, ": ", buffer - debug " Index buffer: ", renderer.scenedata[entity].indexBuffer + debug " Index buffer: ", renderer.scenedata[scene].indexBuffer - for drawable in renderer.scenedata[entity].drawables: - commandBuffer.draw(drawable, vertexBuffers=renderer.scenedata[entity].vertexBuffers, indexBuffer=renderer.scenedata[entity].indexBuffer) + for drawable in renderer.scenedata[scene].drawables.values: + commandBuffer.draw(drawable, vertexBuffers=renderer.scenedata[scene].vertexBuffers, indexBuffer=renderer.scenedata[scene].indexBuffer) if i < renderer.renderPass.subpasses.len - 1: commandBuffer.vkCmdNextSubpass(VK_SUBPASS_CONTENTS_INLINE)
--- a/src/semicongine/vulkan/renderpass.nim Sat Apr 22 17:34:59 2023 +0700 +++ b/src/semicongine/vulkan/renderpass.nim Tue Apr 25 18:23:57 2023 +0700 @@ -70,8 +70,8 @@ device: Device, vertexCode: ShaderCode, fragmentCode: ShaderCode, - inFlightFrames: int = 2, - format = VK_FORMAT_UNDEFINED , + inFlightFrames=2, + format=VK_FORMAT_UNDEFINED , clearColor=Vec4f([0.8'f32, 0.8'f32, 0.8'f32, 1'f32]) ): RenderPass = assert device.vk.valid
--- a/src/semicongine/vulkan/shader.nim Sat Apr 22 17:34:59 2023 +0700 +++ b/src/semicongine/vulkan/shader.nim Tue Apr 25 18:23:57 2023 +0700 @@ -93,7 +93,9 @@ entrypoint=DEFAULT_SHADER_ENTRYPOINT , main: seq[string] ): ShaderCode {.compileTime.} = + var code = @[&"#version {version}", ""] & + # var code = @[&"#version {version}", "layout(row_major) uniform;", ""] & (if inputs.len > 0: inputs.glslInput() & @[""] else: @[]) & (if uniforms.len > 0: uniforms.glslUniforms() & @[""] else: @[]) & (if outputs.len > 0: outputs.glslOutput() & @[""] else: @[]) &