Mercurial > games > semicongine
changeset 653:ac7dfbd56cc6
add: textures now support in shader via scene data, also: improved config handling a bit, more to come
author | Sam <sam@basx.dev> |
---|---|
date | Sat, 06 May 2023 23:06:33 +0700 |
parents | 43240986f76d |
children | 7973a17f76e6 |
files | src/semicongine.nim src/semicongine/config.nim src/semicongine/engine.nim src/semicongine/mesh.nim src/semicongine/renderer.nim src/semicongine/vulkan/descriptor.nim src/semicongine/vulkan/pipeline.nim tests/test_vulkan_wrapper.nim |
diffstat | 8 files changed, 49 insertions(+), 147 deletions(-) [+] |
line wrap: on
line diff
--- a/src/semicongine.nim Sat May 06 01:37:33 2023 +0700 +++ b/src/semicongine.nim Sat May 06 23:06:33 2023 +0700 @@ -1,6 +1,8 @@ +import semicongine/buildconfig +export buildconfig + import semicongine/audio import semicongine/color -import semicongine/config import semicongine/engine import semicongine/entity import semicongine/events @@ -8,12 +10,12 @@ import semicongine/math import semicongine/mesh import semicongine/renderer +import semicongine/settings import semicongine/platform/window import semicongine/vulkan export audio export color -export config export engine export entity export events @@ -21,5 +23,6 @@ export math export mesh export renderer +export settings export window export vulkan
--- a/src/semicongine/config.nim Sat May 06 01:37:33 2023 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,119 +0,0 @@ -import std/logging -import std/parsecfg -import std/strutils -import std/sequtils -import std/parseutils -import std/strformat -import std/tables -import std/os - - -# build configuration -# ===================== - -# compile-time defines, usefull for build-dependent settings -# can be overriden with compiler flags, e.g. -d:Foo=42 -d:Bar=false -# pramas: {.intdefine.} {.strdefine.} {.booldefine.} - -# root of where config files will be searched -# must be relative (to the directory of the binary) -const DEBUG* = not defined(release) -const CONFIGROOT {.strdefine.}: string = "." -assert not isAbsolute(CONFIGROOT) - -const CONFIGEXTENSION {.strdefine.}: string = "ini" - -# by default enable hot-reload of runtime-configuration only in debug builds -const CONFIGHOTRELOAD {.booldefine.}: bool = DEBUG - -# milliseconds to wait between checks for config hotreload -const CONFIGHOTRELOADINTERVAL {.intdefine.}: int = 1000 - - -when CONFIGHOTRELOAD: - var configUpdates: Channel[(string, Config)] - configUpdates.open() - - -# runtime configuration -# ===================== -# namespace is the path from the CONFIGROOT to the according config file without the file extension -# a config file must always have the extension CONFIGEXTENSION -# a fully qualified config identifier can be in the form {namespace}.{section}.{key} -# {key} and {section} may not contain dots - -# a "namespace" is the path from the config root to an *.CONFIGEXTENSION file, without the file extension -# config is a namespace <-> config mapping -var theConfig: Table[string, Config] - -proc configRoot(): string = - joinPath(absolutePath(getAppDir()), CONFIGROOT) - -proc getFile(namespace: string): string = - joinPath(configRoot(), namespace & "." & CONFIGEXTENSION) - -iterator walkConfigNamespaces(): string = - for file in walkDirRec(dir=configRoot(), relative=true, checkDir=true): - if file.endsWith("." & CONFIGEXTENSION): - yield file[0 ..< ^(CONFIGEXTENSION.len + 1)] - -proc loadAllConfig(): Table[string, Config] = - for ns in walkConfigNamespaces(): - result[ns] = ns.getFile().loadConfig() - -proc configStr(key: string, section="", namespace = ""): string = - var ns = namespace - if ns == "": - ns = "config" - when CONFIGHOTRELOAD: - while configUpdates.peek() > 0: - let (updatedNamespace, updatedConfig) = configUpdates.recv() - theConfig[updatedNamespace] = updatedConfig - if not theConfig.hasKey(ns): - raise newException(Exception, &"Namespace {ns} not found, available namespaces are {theConfig.keys().toSeq}") - theConfig[ns].getSectionValue(section, key) - -proc config*[T: int|float|string](key: string, section: string, namespace = "config"): T = - when T is int: - let value = configStr(key, section, namespace) - if parseInt(value, result) == 0: - raise newException(Exception, &"Unable to parse int from config {namespace}.{section}.{key}: {value}") - elif T is float: - let value = configStr(key, section, namespace) - if parseFloat(value, result) == 0: - raise newException(Exception, &"Unable to parse float from config {namespace}.{section}.{key}: {value}") - else: - result = configStr(key, section, namespace) - -proc config*[T: int|float|string](identifier: string): T = - # identifier can be in the form: - # {key} - # {section}.{key} - # {namespace}.{section}.{key} - let parts = identifier.rsplit(".", maxsplit=2) - if parts.len == 1: result = config[T](parts[0], "") - elif parts.len == 2: result = config[T](parts[1], parts[0]) - elif parts.len == 3: result = config[T](parts[2], parts[1], parts[0]) - -theConfig = loadAllConfig() - -when CONFIGHOTRELOAD == true: - import std/times - - proc configFileWatchdog() {.thread.} = - var configModTimes: Table[string, Time] - while true: - for namespace in walkConfigNamespaces(): - if not (namespace in configModTimes): - configModTimes[namespace] = Time() - let lastMod = namespace.getFile().getLastModificationTime() - if lastMod != configModTimes[namespace]: - configUpdates.send((namespace, namespace.getFile().loadConfig())) - sleep CONFIGHOTRELOADINTERVAL - var thethread: Thread[void] - createThread(thethread, configFileWatchdog) - -if DEBUG: - setLogFilter(lvlAll) -else: - setLogFilter(lvlWarn)
--- a/src/semicongine/engine.nim Sat May 06 01:37:33 2023 +0700 +++ b/src/semicongine/engine.nim Sat May 06 23:06:33 2023 +0700 @@ -1,4 +1,6 @@ import std/sequtils +import std/strutils +import std/logging import std/os import ./platform/window @@ -13,7 +15,7 @@ import ./entity import ./renderer import ./events -import ./config +import ./buildconfig import ./math import ./audio @@ -64,6 +66,9 @@ resizeHandler: proc(engine: var Engine) = nil, eventHandler: proc(engine: var Engine, event: Event) = nil ): Engine = + echo "Set log level to ", ENGINE_LOGLEVEL + setLogFilter(ENGINE_LOGLEVEL) + result.state = Starting result.exitHandler = exitHandler result.resizeHandler = resizeHandler
--- a/src/semicongine/mesh.nim Sat May 06 01:37:33 2023 +0700 +++ b/src/semicongine/mesh.nim Sat May 06 23:06:33 2023 +0700 @@ -140,6 +140,11 @@ mesh.data[attribute] = DataList(thetype: getDataType[T]()) setValues(mesh.data[attribute], data) +proc updateMeshData*[T: GPUType|int|uint|float](mesh: var Mesh, attribute: string, data: seq[T]) = + assert attribute in mesh.data + mesh.changedAttributes.add attribute + 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
--- a/src/semicongine/renderer.nim Sat May 06 01:37:33 2023 +0700 +++ b/src/semicongine/renderer.nim Sat May 06 23:06:33 2023 +0700 @@ -1,6 +1,7 @@ import std/options import std/tables import std/strformat +import std/strutils import std/logging import ./vulkan/api @@ -18,6 +19,7 @@ import ./mesh import ./gpu_data import ./math +import ./buildconfig type SceneData = object @@ -26,7 +28,7 @@ indexBuffer*: Buffer uniformBuffers*: seq[Buffer] # one per frame-in-flight images*: seq[Image] # used to back texturees - textures*: seq[Table[string, Texture]] # per frame-in-flight + textures*: Table[string, Texture] # per frame-in-flight attributeLocation*: Table[string, MemoryPerformanceHint] attributeBindingNumber*: Table[string, int] transformAttribute: string # name of attribute that is used for per-instance mesh transformation @@ -155,29 +157,26 @@ data.drawables[mesh] = drawable # setup uniforms and textures - for i in 0 ..< renderer.renderPass.subpasses.len: - var subpass = renderer.renderPass.subpasses[i] + for subpass_i in 0 ..< renderer.renderPass.subpasses.len: + var subpass = renderer.renderPass.subpasses[subpass_i] for pipeline in subpass.pipelines.mitems: var uniformBufferSize = 0'u64 for uniform in pipeline.uniforms: uniformBufferSize += uniform.thetype.size if uniformBufferSize > 0: - for i in 0 ..< renderer.swapchain.inFlightFrames: + for frame_i in 0 ..< renderer.swapchain.inFlightFrames: data.uniformBuffers.add renderer.device.createBuffer( size=uniformBufferSize, usage=[VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT], requireMappable=true, preferVRAM=true, ) - for i in 0 ..< renderer.swapchain.inFlightFrames: - var textures: Table[string, Texture] - # todo: get textures from scene, currently only 32 bit images supported - for name, image in scene.textures.pairs: - textures[name] = renderer.device.createTexture(image.width, image.height, 4, addr image.imagedata[0][0]) - data.textures.add textures - # need a separate descriptor for each frame in flight + + for name, image in scene.textures.pairs: + data.textures[name] = renderer.device.createTexture(image.width, image.height, 4, addr image.imagedata[0][0]) pipeline.setupDescriptors(data.uniformBuffers, data.textures, inFlightFrames=renderer.swapchain.inFlightFrames) - pipeline.descriptorSets[i].writeDescriptorSet() + for frame_i in 0 ..< renderer.swapchain.inFlightFrames: + pipeline.descriptorSets[frame_i].writeDescriptorSet() renderer.scenedata[scene] = data @@ -251,6 +250,7 @@ for pipeline in subpass.pipelines: var mpipeline = pipeline commandBuffer.vkCmdBindPipeline(subpass.pipelineBindPoint, mpipeline.vk) + echo "############## ", mpipeline.descriptorSets[renderer.swapchain.currentInFlight] commandBuffer.vkCmdBindDescriptorSets(subpass.pipelineBindPoint, mpipeline.layout, 0, 1, addr(mpipeline.descriptorSets[renderer.swapchain.currentInFlight].vk), 0, nil) debug "Scene buffers:"
--- a/src/semicongine/vulkan/descriptor.nim Sat May 06 01:37:33 2023 +0700 +++ b/src/semicongine/vulkan/descriptor.nim Sat May 06 23:06:33 2023 +0700 @@ -170,4 +170,7 @@ pImageInfo: addr imageInfos[^1], ) inc i + echo "wrote descriptors:" + for w in descriptorSetWrites: + echo " ", w descriptorSet.layout.device.vk.vkUpdateDescriptorSets(uint32(descriptorSetWrites.len), descriptorSetWrites.toCPointer, 0, nil)
--- a/src/semicongine/vulkan/pipeline.nim Sat May 06 01:37:33 2023 +0700 +++ b/src/semicongine/vulkan/pipeline.nim Sat May 06 23:06:33 2023 +0700 @@ -36,10 +36,9 @@ uniformList[attribute.name] = attribute result = uniformList.values.toSeq -proc setupDescriptors*(pipeline: var Pipeline, buffers: seq[Buffer], textures: seq[Table[string, Texture]], inFlightFrames: int) = +proc setupDescriptors*(pipeline: var Pipeline, buffers: seq[Buffer], textures: Table[string, Texture], inFlightFrames: int) = assert pipeline.vk.valid assert buffers.len == 0 or buffers.len == inFlightFrames - assert textures.len == 0 or textures.len == inFlightFrames assert pipeline.descriptorSets.len > 0 for i in 0 ..< inFlightFrames: @@ -54,10 +53,10 @@ descriptor.size = size offset += size elif descriptor.thetype == ImageSampler: - if not (descriptor.name in textures[i]): + if not (descriptor.name in textures): raise newException(Exception, "Missing shader texture in scene: " & descriptor.name) - descriptor.imageview = textures[i][descriptor.name].imageView - descriptor.sampler = textures[i][descriptor.name].sampler + descriptor.imageview = textures[descriptor.name].imageView + descriptor.sampler = textures[descriptor.name].sampler proc createPipeline*(device: Device, renderPass: VkRenderPass, vertexCode: ShaderCode, fragmentCode: ShaderCode, inFlightFrames: int, subpass = 0'u32): Pipeline = assert renderPass.valid
--- a/tests/test_vulkan_wrapper.nim Sat May 06 01:37:33 2023 +0700 +++ b/tests/test_vulkan_wrapper.nim Sat May 06 23:06:33 2023 +0700 @@ -100,11 +100,16 @@ var r = rect(color="ff0000") var t = tri(color="0000ff") var c = circle(color="00ff00") - setInstanceData[Vec3f](r, "translate", @[newVec3f(0.5, -0.3)]) - setInstanceData[Vec3f](t, "translate", @[newVec3f(0.3, 0.3)]) - setInstanceData[Vec3f](c, "translate", @[newVec3f(-0.3, 0.1)]) + r.setInstanceData("translate", @[newVec3f(0.5, -0.3)]) + t.setInstanceData("translate", @[newVec3f(0.3, 0.3)]) + c.setInstanceData("translate", @[newVec3f(-0.3, 0.1)]) result = newEntity("root", t, r, c) +proc scene_flag(): Entity = + var r = rect(color="ff0000") + r.updateMeshData("color", @[newVec3f(0, 0), newVec3f(1, 0), newVec3f(1, 1), newVec3f(0, 1)]) + result = newEntity("root", r) + proc main() = var engine = initEngine("Test") @@ -142,16 +147,17 @@ var scenes = [ newScene("simple", scene_simple()), newScene("different mesh types", scene_different_mesh_types()), - newScene("primitives", scene_primitives()) + newScene("primitives", scene_primitives()), + newScene("flag", scene_flag()), ] for scene in scenes.mitems: scene.addShaderGlobal("time", 0.0'f32) - let (R, W) = ([0'u8, 0'u8, 0'u8, 0'u8], [0'u8, 0'u8, 0'u8, 0'u8]) + let (R, W) = ([255'u8, 0'u8, 0'u8, 255'u8], [255'u8, 255'u8, 255'u8, 255'u8]) scene.addTexture("my_little_texture", TextureImage(width: 5, height: 5, imagedata: @[ R, R, R, R, R, - R, R, R, R, R, - R, R, R, R, R, - R, R, R, R, R, + R, R, W, R, R, + R, W, W, W, R, + R, R, W, R, R, R, R, R, R, R, ])) engine.addScene(scene, vertexInput)