# HG changeset patch # User Sam # Date 1673799834 -25200 # Node ID 0c18638c7217097ca19d3639f3ba8a177546893c # Parent 680c4b8ca28aad03fc68d329b613199297278f68 did: refactoring, move more from make to nimscript diff -r 680c4b8ca28a -r 0c18638c7217 Makefile --- a/Makefile Sat Jan 14 23:34:50 2023 +0700 +++ b/Makefile Sun Jan 15 23:23:54 2023 +0700 @@ -1,53 +1,9 @@ -SOURCES := $(shell find src -name '*.nim') - # compilation requirements examples/glslangValidator: thirdparty/bin/linux/glslangValidator cp $< examples examples/glslangValidator.exe: thirdparty/bin/windows/glslangValidator.exe cp $< examples -# build hello_triangle -build/debug/linux/hello_triangle: ${SOURCES} examples/hello_triangle.nim examples/glslangValidator - nim build_linux_debug -o:$@ examples/hello_triangle.nim -build/release/linux/hello_triangle: ${SOURCES} examples/hello_triangle.nim examples/glslangValidator - nim build_linux_release -o:$@ examples/hello_triangle.nim -build/debug/windows/hello_triangle.exe: ${SOURCES} examples/hello_triangle.nim examples/glslangValidator.exe - nim build_windows_debug -o:$@ examples/hello_triangle.nim -build/release/windows/hello_triangle.exe: ${SOURCES} examples/hello_triangle.nim examples/glslangValidator.exe - nim build_windows_release -o:$@ examples/hello_triangle.nim - -build_all_linux_hello_triangle: build/debug/linux/hello_triangle build/release/linux/hello_triangle -build_all_windows_hello_triangle: build/debug/windows/hello_triangle.exe build/release/windows/hello_triangle.exe -build_all_hello_triangle: build_all_linux_hello_triangle build_all_windows_hello_triangle - -# build alotof_triangles -build/debug/linux/alotof_triangles: ${SOURCES} examples/alotof_triangles.nim examples/glslangValidator - nim build_linux_debug -o:$@ examples/alotof_triangles.nim -build/release/linux/alotof_triangles: ${SOURCES} examples/alotof_triangles.nim examples/glslangValidator - nim build_linux_release -o:$@ examples/alotof_triangles.nim -build/debug/windows/alotof_triangles.exe: ${SOURCES} examples/alotof_triangles.nim examples/glslangValidator.exe - nim build_windows_debug -o:$@ examples/alotof_triangles.nim -build/release/windows/alotof_triangles.exe: ${SOURCES} examples/alotof_triangles.nim examples/glslangValidator.exe - nim build_windows_release -o:$@ examples/alotof_triangles.nim - -build_all_linux_alotof_triangles: build/debug/linux/alotof_triangles build/release/linux/alotof_triangles -build_all_windows_alotof_triangles: build/debug/windows/alotof_triangles.exe build/release/windows/alotof_triangles.exe -build_all_alotof_triangles: build_all_linux_alotof_triangles build_all_windows_alotof_triangles - -# clean -clean: - rm -rf build - rm -rf thirdparty - -# tests -.PHONY: tests -tests: - testament p tests/ - -# publish -publish: - rsync -rv build/ basx.dev:/var/www/public.basx.dev/zamikongine - # download thirdparty-libraries thirdparty/bin/linux/glslangValidator: diff -r 680c4b8ca28a -r 0c18638c7217 config.nims --- a/config.nims Sat Jan 14 23:34:50 2023 +0700 +++ b/config.nims Sun Jan 15 23:23:54 2023 +0700 @@ -1,6 +1,12 @@ -import os +import std/strformat +import std/strutils +import std/os -const buildbase = "build" +const BUILDBASE = "build" +const DEBUG = "debug" +const RELEASE = "release" +const LINUX = "linux" +const WINDOWS = "windows" proc compilerFlags() = switch("path", "src") @@ -19,30 +25,85 @@ switch("checks", "off") switch("assertions", "off") -task build_linux_debug, "build linux debug": +task single_linux_debug, "build linux debug": compilerFlags() compilerFlagsDebug() - buildbase.joinPath("debug/linux").mkDir() + switch("outdir", BUILDBASE / DEBUG / LINUX) + setCommand "c" + mkDir(BUILDBASE / DEBUG / LINUX) + +task single_linux_release, "build linux release": + compilerFlags() + compilerFlagsRelease() + switch("outdir", BUILDBASE / RELEASE / LINUX) setCommand "c" + mkDir(BUILDBASE / RELEASE / LINUX) -task build_linux_release, "build linux release": +task single_windows_debug, "build windows debug": + compilerFlags() + compilerFlagsDebug() + # for some the --define:mingw does not work from inside here... + # so we need to set it when calling the task and use "/" to prevent + # the use of backslash while crosscompiling + switch("define", "mingw") + switch("outdir", BUILDBASE & "/" & DEBUG & "/" & WINDOWS) + setCommand "c" + mkDir(BUILDBASE & "/" & DEBUG & "/" & WINDOWS) + +task single_windows_release, "build windows release": compilerFlags() compilerFlagsRelease() - buildbase.joinPath("release/linux").mkDir() - setCommand "c" - -task build_windows_debug, "build windows debug": - compilerFlags() - compilerFlagsDebug() + switch("outdir", BUILDBASE & "/" & RELEASE & "/" & WINDOWS) switch("define", "mingw") - buildbase.joinPath("debug/windows").mkDir() setCommand "c" + mkDir(BUILDBASE & "/" & RELEASE & "/" & WINDOWS) -task build_windows_release, "build windows release": +task build_all_linux_debug, "build all examples with linux/debug": + for file in listFiles("examples"): + if file.endsWith(".nim"): + selfExec(&"single_linux_debug {file}") + +task build_all_linux_release, "build all examples with linux/release": + for file in listFiles("examples"): + if file.endsWith(".nim"): + selfExec(&"single_linux_release {file}") + +task build_all_windows_debug, "build all examples with windows/debug": + for file in listFiles("examples"): + if file.endsWith(".nim"): + exec(&"nim single_windows_debug --define:mingw {file}") + +task build_all_windows_release, "build all examples with windows/release": + for file in listFiles("examples"): + if file.endsWith(".nim"): + exec(&"nim single_windows_release --define:mingw {file}") + +task build_all_debug, "build all examples with */debug": + build_all_linux_debugTask() + build_all_windows_debugTask() + +task build_all_release, "build all examples with */release": + build_all_linux_releaseTask() + build_all_windows_releaseTask() + +task build_all_linux, "build all examples with linux/*": + build_all_linux_debugTask() + build_all_linux_releaseTask() + +task build_all_windows, "build all examples with windows/*": + build_all_windows_debugTask() + build_all_windows_releaseTask() + +task build_all, "build all examples": + build_all_linuxTask() + build_all_windowsTask() + +task clean, "remove all build files": + exec(&"rm -rf {BUILDBASE}") + +task publish, "publish all build": + exec("rsync -rv build/ basx.dev:/var/www/public.basx.dev/zamikongine") + + +if getCommand() == "c": compilerFlags() - compilerFlagsRelease() - switch("define", "mingw") - buildbase.joinPath("release/windows").mkDir() - setCommand "c" - -compilerFlags() diff -r 680c4b8ca28a -r 0c18638c7217 examples/hello_triangle.nim --- a/examples/hello_triangle.nim Sat Jan 14 23:34:50 2023 +0700 +++ b/examples/hello_triangle.nim Sun Jan 15 23:23:54 2023 +0700 @@ -17,25 +17,11 @@ VertexDataA = object position: PositionAttribute[Vec2[float32]] color: ColorAttribute[Vec3[float32]] - Uniforms = object - mat: Descriptor[Mat44[float32]] - dt: Descriptor[float32] -var pipeline: RenderPipeline[VertexDataA, Uniforms] +var pipeline: RenderPipeline[VertexDataA, void] -var pos = 0'f32; -var uniforms = Uniforms( - mat: Descriptor[Mat44[float32]](value: Unit44f32), - dt: Descriptor[float32](value: 0'f32), -) -var scaledir = 1'f32 proc globalUpdate(engine: var Engine, dt: float32) = - uniforms.mat.value = uniforms.mat.value * scale3d(1 + scaledir * dt, 1 + scaledir * dt, 0'f32) - if uniforms.mat.value[0, 0] > 2'f32 or uniforms.mat.value[0, 0] < 0.5'f32: - scaledir = - scaledir - for buffer in pipeline.uniformBuffers: - buffer.updateData(uniforms) - echo uniforms.mat.value + discard # vertex data (types must match the above VertexAttributes) const @@ -65,22 +51,11 @@ triangle.parts.add trianglemesh # upload data, prepare shaders, etc - const vertexShader = generateVertexShaderCode[VertexDataA, Uniforms]( - # have 1 at: - # [2][0] [0][3] - # "out_position = vec4(in_position[0] + uniforms.mat[0][0], in_position[1] + uniforms.mat[0][0], 0, 1);" - "out_position = uniforms.mat * vec4(in_position, 0, 1);" - # "out_position = vec4(in_position, 0, 1);" + const vertexShader = generateVertexShaderCode[VertexDataA, void]( + # "out_position = uniforms.mat * vec4(in_position, 0, 1);" ) const fragmentShader = generateFragmentShaderCode[VertexDataA]() - static: - echo "--------------" - for (i, line) in enumerate(vertexShader.splitLines()): - echo $(i + 1) & " " & line - echo "--------------" - echo fragmentShader - echo "--------------" - pipeline = setupPipeline[VertexDataA, Uniforms, uint16]( + pipeline = setupPipeline[VertexDataA, void, uint16]( myengine, triangle, vertexShader, diff -r 680c4b8ca28a -r 0c18638c7217 examples/squares.nim --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/squares.nim Sun Jan 15 23:23:54 2023 +0700 @@ -0,0 +1,109 @@ +import std/times +import std/strutils +import std/math +import std/random +import std/enumerate + +import zamikongine/engine +import zamikongine/math/vector +import zamikongine/math/matrix +import zamikongine/vertex +import zamikongine/descriptor +import zamikongine/mesh +import zamikongine/thing +import zamikongine/shader +import zamikongine/buffer + +type + VertexDataA = object + position11: PositionAttribute[Vec2[float32]] + color22: ColorAttribute[Vec3[float32]] + index: GenericAttribute[uint32] + Uniforms = object + t: Descriptor[float32] + +var + pipeline: RenderPipeline[VertexDataA, Uniforms] + uniformdata = Uniforms(t: Descriptor[float32](value: 0'f32)) + +proc globalUpdate(engine: var Engine, dt: float32) = + uniformdata.t.value += dt + for buffer in pipeline.uniformBuffers: + buffer.updateData(uniformdata) + +when isMainModule: + randomize() + var myengine = igniteEngine("A lot of triangles") + const + COLUMNS = 10 + ROWS = 10 + WIDTH = 2'f32 / COLUMNS + HEIGHT = 2'f32 / ROWS + var + vertices: array[COLUMNS * ROWS * 4, Vec2[float32]] + colors: array[COLUMNS * ROWS * 4, Vec3[float32]] + iValues: array[COLUMNS * ROWS * 4, uint32] + indices: array[COLUMNS * ROWS * 2, array[3, uint16]] + + for row in 0 ..< ROWS: + for col in 0 ..< COLUMNS: + let + y: float32 = (row * 2 / COLUMNS) - 1 + x: float32 = (col * 2 / ROWS) - 1 + color = Vec3[float32]([(x + 1) / 2, (y + 1) / 2, 0'f32]) + squareIndex = row * COLUMNS + col + vertIndex = squareIndex * 4 + vertices[vertIndex + 0] = Vec2[float32]([x, y]) + vertices[vertIndex + 1] = Vec2[float32]([x + WIDTH, y]) + vertices[vertIndex + 2] = Vec2[float32]([x + WIDTH, y + HEIGHT]) + vertices[vertIndex + 3] = Vec2[float32]([x, y + HEIGHT]) + colors[vertIndex + 0] = color + colors[vertIndex + 1] = color + colors[vertIndex + 2] = color + colors[vertIndex + 3] = color + iValues[vertIndex + 0] = uint32(squareIndex) + iValues[vertIndex + 1] = uint32(squareIndex) + iValues[vertIndex + 2] = uint32(squareIndex) + iValues[vertIndex + 3] = uint32(squareIndex) + indices[squareIndex * 2 + 0] = [uint16(vertIndex + 0), uint16(vertIndex + 1), uint16(vertIndex + 2)] + indices[squareIndex * 2 + 1] = [uint16(vertIndex + 2), uint16(vertIndex + 3), uint16(vertIndex + 0)] + + var scene = new Thing + + type PIndexedMesh = ref IndexedMesh[VertexDataA, uint16] # required so we can use ctor with ref/on heap + var squaremesh = PIndexedMesh( + vertexData: VertexDataA( + position11: PositionAttribute[Vec2[float32]](data: @vertices), + color22: ColorAttribute[Vec3[float32]](data: @colors), + index: GenericAttribute[uint32](data: @iValues), + ), + indices: @indices + ) + var childthing = new Thing + childthing.parts.add squaremesh + scene.children.add childthing + + const vertexShader = generateVertexShaderCode[VertexDataA, Uniforms]( + """ + float pos_weight = index / 100.0; // add some gamma correction? + float t = sin(uniforms.t * 0.5) * 0.5 + 0.5; + float v = min(1, max(0, pow(pos_weight - t, 2))); + v = pow(1 - v, 3000); + out_color = vec3(in_color.r, in_color.g, v * 0.5); + """ + ) + const fragmentShader = generateFragmentShaderCode[VertexDataA]() + static: + echo "--------------" + for (i, line) in enumerate(vertexShader.splitLines()): + echo $(i + 1) & " " & line + echo "--------------" + pipeline = setupPipeline[VertexDataA, Uniforms, uint16]( + myengine, + scene, + vertexShader, + fragmentShader + ) + myengine.run(pipeline, globalUpdate) + pipeline.trash() + myengine.trash() diff -r 680c4b8ca28a -r 0c18638c7217 notes --- a/notes Sat Jan 14 23:34:50 2023 +0700 +++ b/notes Sun Jan 15 23:23:54 2023 +0700 @@ -17,7 +17,8 @@ Rendering: -- [ ] Uniforms +- [x] Uniforms +- [ ] Per-instance vertex attributes - [ ] Textures - [ ] Depth buffering diff -r 680c4b8ca28a -r 0c18638c7217 src/zamikongine/buffer.nim --- a/src/zamikongine/buffer.nim Sat Jan 14 23:34:50 2023 +0700 +++ b/src/zamikongine/buffer.nim Sun Jan 15 23:23:54 2023 +0700 @@ -21,9 +21,11 @@ proc trash*(buffer: var Buffer) = assert int64(buffer.vkBuffer) != 0 - assert int64(buffer.memory) != 0 vkDestroyBuffer(buffer.device, buffer.vkBuffer, nil) buffer.vkBuffer = VkBuffer(0) + if buffer.size == 0: # for zero-size buffers there are no memory allocations + return + assert int64(buffer.memory) != 0 vkFreeMemory(buffer.device, buffer.memory, nil) buffer.memory = VkDeviceMemory(0) @@ -65,7 +67,8 @@ allocationSize: result.memoryRequirements.size, memoryTypeIndex: result.findMemoryType(physicalDevice, VkMemoryPropertyFlags(memoryProperties)) ) - checkVkResult result.device.vkAllocateMemory(addr(allocInfo), nil, addr(result.memory)) + if result.size > 0: + checkVkResult result.device.vkAllocateMemory(addr(allocInfo), nil, addr(result.memory)) checkVkResult result.device.vkBindBufferMemory(result.vkBuffer, result.memory, VkDeviceSize(0)) if persistentMapping: checkVkResult vkMapMemory( diff -r 680c4b8ca28a -r 0c18638c7217 src/zamikongine/descriptor.nim --- a/src/zamikongine/descriptor.nim Sat Jan 14 23:34:50 2023 +0700 +++ b/src/zamikongine/descriptor.nim Sun Jan 15 23:23:54 2023 +0700 @@ -33,8 +33,8 @@ ) checkVkResult device.vkCreateDescriptorSetLayout(addr(layoutInfo), nil, addr(result)) -proc createUniformBuffers*[nBuffers: static int, T](device: VkDevice, physicalDevice: VkPhysicalDevice): array[nBuffers, Buffer] = - let size = sizeof(T) +proc createUniformBuffers*[nBuffers: static int, Uniforms](device: VkDevice, physicalDevice: VkPhysicalDevice): array[nBuffers, Buffer] = + let size = sizeof(Uniforms) for i in 0 ..< nBuffers: var buffer = InitBuffer( device, @@ -51,14 +51,15 @@ func generateGLSLUniformDeclarations*[Uniforms](binding: int = 0): string {.compileTime.} = var stmtList: seq[string] - let uniformTypeName = name(Uniforms).toUpper() - let uniformInstanceName = name(Uniforms).toLower() - stmtList.add(&"layout(binding = {binding}) uniform {uniformTypeName} {{") - for fieldname, value in Uniforms().fieldPairs: - when typeof(value) is Descriptor: - let glsltype = getGLSLType[getDescriptorType(value)]() - let n = fieldname - stmtList.add(&" {glsltype} {n};") - stmtList.add(&"}} {uniformInstanceName};") + when not (Uniforms is void): + let uniformTypeName = name(Uniforms).toUpper() + let uniformInstanceName = name(Uniforms).toLower() + stmtList.add(&"layout(binding = {binding}) uniform {uniformTypeName} {{") + for fieldname, value in Uniforms().fieldPairs: + when typeof(value) is Descriptor: + let glsltype = getGLSLType[getDescriptorType(value)]() + let n = fieldname + stmtList.add(&" {glsltype} {n};") + stmtList.add(&"}} {uniformInstanceName};") return stmtList.join("\n") diff -r 680c4b8ca28a -r 0c18638c7217 src/zamikongine/engine.nim --- a/src/zamikongine/engine.nim Sat Jan 14 23:34:50 2023 +0700 +++ b/src/zamikongine/engine.nim Sun Jan 15 23:23:54 2023 +0700 @@ -501,7 +501,7 @@ ) = result.vulkan.device.device.setupSyncPrimitives() -proc setupPipeline*[VertexType, UniformType: object, IndexType: uint16|uint32](engine: var Engine, scenedata: ref Thing, vertexShader, fragmentShader: static string): RenderPipeline[VertexType, UniformType] = +proc setupPipeline*[VertexType; UniformType; IndexType: uint16|uint32](engine: var Engine, scenedata: ref Thing, vertexShader, fragmentShader: static string): RenderPipeline[VertexType, UniformType] = engine.currentscenedata = scenedata result = initRenderPipeline[VertexType, UniformType]( engine.vulkan.device.device, @@ -576,7 +576,7 @@ vkUpdateDescriptorSets(result.device, 1, addr(descriptorWrite), 0, nil) -proc runPipeline(commandBuffer: VkCommandBuffer, pipeline: var RenderPipeline, currentFrame: int) = +proc runPipeline[VertexType; Uniforms](commandBuffer: VkCommandBuffer, pipeline: var RenderPipeline[VertexType, Uniforms], currentFrame: int) = vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline) vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.layout, 0, 1, addr(pipeline.descriptors[currentFrame]), 0, nil) @@ -588,7 +588,7 @@ vertexBuffers.add buffer.vkBuffer offsets.add VkDeviceSize(0) - vkCmdBindVertexBuffers(commandBuffer, firstBinding=0'u32, bindingCount=2'u32, pBuffers=addr(vertexBuffers[0]), pOffsets=addr(offsets[0])) + vkCmdBindVertexBuffers(commandBuffer, firstBinding=0'u32, bindingCount=uint32(vertexBuffers.len), pBuffers=addr(vertexBuffers[0]), pOffsets=addr(offsets[0])) vkCmdDraw(commandBuffer, vertexCount=vertexCount, instanceCount=1'u32, firstVertex=0'u32, firstInstance=0'u32) for (vertexBufferSet, indexBuffer, indicesCount, indexType) in pipeline.indexedVertexBuffers: @@ -599,7 +599,7 @@ vertexBuffers.add buffer.vkBuffer offsets.add VkDeviceSize(0) - vkCmdBindVertexBuffers(commandBuffer, firstBinding=0'u32, bindingCount=2'u32, pBuffers=addr(vertexBuffers[0]), pOffsets=addr(offsets[0])) + vkCmdBindVertexBuffers(commandBuffer, firstBinding=0'u32, bindingCount=uint32(vertexBuffers.len), pBuffers=addr(vertexBuffers[0]), pOffsets=addr(offsets[0])) vkCmdBindIndexBuffer(commandBuffer, indexBuffer.vkBuffer, VkDeviceSize(0), indexType) vkCmdDrawIndexed(commandBuffer, indicesCount, 1, 0, 0, 0) diff -r 680c4b8ca28a -r 0c18638c7217 src/zamikongine/mesh.nim --- a/src/zamikongine/mesh.nim Sat Jan 14 23:34:50 2023 +0700 +++ b/src/zamikongine/mesh.nim Sun Jan 15 23:23:54 2023 +0700 @@ -4,6 +4,7 @@ import ./thing import ./buffer import ./vertex +import ./math/vector type Mesh*[T] = object of Part @@ -101,3 +102,10 @@ result[2] = uint32(mesh.indices.len * mesh.indices[0].len) result[3] = getVkIndexType(mesh) + +func squareData*[T:SomeFloat](): auto = PositionAttribute[Vec2[T]]( + data: @[Vec2[T]([T(0), T(0)]), Vec2[T]([T(0), T(1)]), Vec2[T]([T(1), T(1)]), Vec2[T]([T(1), T(0)])] +) +func squareIndices*[T:uint16|uint32](): auto = seq[array[3, T]]( + @[[T(1), T(0), T(3)], [T(2), T(1), T(3)], ] +) diff -r 680c4b8ca28a -r 0c18638c7217 src/zamikongine/shader.nim --- a/src/zamikongine/shader.nim Sat Jan 14 23:34:50 2023 +0700 +++ b/src/zamikongine/shader.nim Sun Jan 15 23:23:54 2023 +0700 @@ -35,8 +35,6 @@ let stagename = stage2string(stage) # TODO: compiles only on linux for now (because we don't have compile-time functionality in std/tempfile) - if not defined(linux): - raise newException(Exception, "Compilation is currently only supported on linux (need mktemp command), sorry!") let (tmpfile, exitCode) = gorgeEx(command=fmt"mktemp --tmpdir shader_XXXXXXX.{stagename}") if exitCode != 0: raise newException(Exception, tmpfile) diff -r 680c4b8ca28a -r 0c18638c7217 src/zamikongine/thing.nim --- a/src/zamikongine/thing.nim Sat Jan 14 23:34:50 2023 +0700 +++ b/src/zamikongine/thing.nim Sun Jan 15 23:23:54 2023 +0700 @@ -1,5 +1,4 @@ {.experimental: "codeReordering".} -import std/times type Part* = object of RootObj diff -r 680c4b8ca28a -r 0c18638c7217 src/zamikongine/vertex.nim --- a/src/zamikongine/vertex.nim Sat Jan 14 23:34:50 2023 +0700 +++ b/src/zamikongine/vertex.nim Sun Jan 15 23:23:54 2023 +0700 @@ -14,9 +14,9 @@ Unknown, Position Color GenericAttribute*[T:VertexAttributeType] = object data*: seq[T] - PositionAttribute*[T:VertexAttributeType] = object + PositionAttribute*[T:Vec] = object data*: seq[T] - ColorAttribute*[T:VertexAttributeType] = object + ColorAttribute*[T:Vec] = object data*: seq[T] VertexAttribute* = GenericAttribute|PositionAttribute|ColorAttribute @@ -85,6 +85,11 @@ else: assert result == uint32(value.data.len) +func VertexAttributesCount*[T](): uint32 = + for name, value in T().fieldPairs: + when typeof(value) is VertexAttribute: + result += 1 + func generateGLSLVertexDeclarations*[T](): string = var stmtList: seq[string] var i = 0