# HG changeset patch # User Sam # Date 1673680080 -25200 # Node ID 54a1f8ee208e1f041460952fd31b52a7f56a2582 # Parent 455f1a416fe763e12bd6625414ddb6f64a13273f big refactoring, part1 diff -r 455f1a416fe7 -r 54a1f8ee208e examples/alotof_triangles.nim --- a/examples/alotof_triangles.nim Wed Jan 11 11:55:54 2023 +0700 +++ b/examples/alotof_triangles.nim Sat Jan 14 14:08:00 2023 +0700 @@ -1,3 +1,4 @@ +import std/times import std/math import std/random @@ -14,6 +15,9 @@ position11: VertexAttribute[Vec2[float32]] color22: VertexAttribute[Vec3[float32]] +proc globalUpdate(engine: var Engine, dt: Duration) = + discard + proc randomtransform(): Mat33[float32] = let randomscale = scale2d(float32(rand(1.0) + 0.5), float32(rand(1.0) + 0.5)) let randomrotate = rotate2d(float32(rand(2 * PI))) @@ -22,7 +26,7 @@ when isMainModule: randomize() - var myengine = igniteEngine() + var myengine = igniteEngine("A lot of triangles") const baseTriangle = [ Vec3([-0.1'f32, -0.1'f32, 1'f32]), Vec3([ 0.1'f32, 0.1'f32, 1'f32]), @@ -34,6 +38,7 @@ for i in 1 .. 300: var randommesh = new Mesh[VertexDataA] # TODO: create randomized position11 from baseTriangle with random transformation matrix + let randomcolor1 = Vec3([float32(rand(1)), float32(rand(1)), float32(rand(1))]) let transform1 = randomtransform() randommesh.vertexData = VertexDataA( position11: VertexAttribute[Vec2[float32]]( @@ -44,14 +49,11 @@ ] ), color22: VertexAttribute[Vec3[float32]]( - data: @[ - Vec3([float32(rand(1)), float32(rand(1)), float32(rand(1))]), - Vec3([float32(rand(1)), float32(rand(1)), float32(rand(1))]), - Vec3([float32(rand(1)), float32(rand(1)), float32(rand(1))]), - ] + data: @[randomcolor1, randomcolor1, randomcolor1] ) ) + let randomcolor2 = Vec3([float32(rand(1)), float32(rand(1)), float32(rand(1))]) let transform2 = randomtransform() var randomindexedmesh = new IndexedMesh[VertexDataA, uint16] randomindexedmesh.vertexData = VertexDataA( @@ -63,11 +65,7 @@ ] ), color22: VertexAttribute[Vec3[float32]]( - data: @[ - Vec3([float32(rand(1)), float32(rand(1)), float32(rand(1))]), - Vec3([float32(rand(1)), float32(rand(1)), float32(rand(1))]), - Vec3([float32(rand(1)), float32(rand(1)), float32(rand(1))]), - ] + data: @[randomcolor2, randomcolor2, randomcolor2] ) ) randomindexedmesh.indices = @[[0'u16, 1'u16, 2'u16]] @@ -76,11 +74,12 @@ childthing.parts.add randomindexedmesh scene.children.add childthing - setupPipeline[VertexDataA, uint16]( + var pipeline = setupPipeline[VertexDataA, float32, float32, uint16]( myengine, scene, generateVertexShaderCode[VertexDataA]("main", "position11", "color22"), generateFragmentShaderCode[VertexDataA]("main"), ) - myengine.fullThrottle() + myengine.run(pipeline, globalUpdate) + pipeline.trash() myengine.trash() diff -r 455f1a416fe7 -r 54a1f8ee208e examples/hello_triangle.nim --- a/examples/hello_triangle.nim Wed Jan 11 11:55:54 2023 +0700 +++ b/examples/hello_triangle.nim Sat Jan 14 14:08:00 2023 +0700 @@ -1,15 +1,26 @@ +import std/times + import zamikongine/engine import zamikongine/math/vector import zamikongine/vertex import zamikongine/mesh import zamikongine/thing import zamikongine/shader +import zamikongine/buffer type # define type of vertex VertexDataA = object position: VertexAttribute[Vec2[float32]] color: VertexAttribute[Vec3[float32]] + UniformType = float32 + +proc globalUpdate(engine: var Engine, dt: Duration) = + # var t = float32(dt.inNanoseconds) / 1_000_000_000'f32 + # for buffer in engine.vulkan.uniformBuffers: + # buffer.updateData(t) + + echo dt # vertex data (types must match the above VertexAttributes) const @@ -25,7 +36,7 @@ ] when isMainModule: - var myengine = igniteEngine() + var myengine = igniteEngine("Hello triangle") # build a mesh var trianglemesh = new Mesh[VertexDataA] @@ -39,12 +50,13 @@ triangle.parts.add trianglemesh # upload data, prepare shaders, etc - setupPipeline[VertexDataA, uint16]( + var pipeline = setupPipeline[VertexDataA, UniformType, float32, uint16]( myengine, triangle, generateVertexShaderCode[VertexDataA]("main", "position", "color"), generateFragmentShaderCode[VertexDataA]("main"), ) # show something - myengine.fullThrottle() + myengine.run(pipeline, globalUpdate) + pipeline.trash() myengine.trash() diff -r 455f1a416fe7 -r 54a1f8ee208e src/zamikongine/buffer.nim --- a/src/zamikongine/buffer.nim Wed Jan 11 11:55:54 2023 +0700 +++ b/src/zamikongine/buffer.nim Sat Jan 14 14:08:00 2023 +0700 @@ -6,6 +6,7 @@ None = 0 TransferSrc = VK_BUFFER_USAGE_TRANSFER_SRC_BIT TransferDst = VK_BUFFER_USAGE_TRANSFER_DST_BIT + UniformBuffer = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT IndexBuffer = VK_BUFFER_USAGE_INDEX_BUFFER_BIT VertexBuffer = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT Buffer* = object @@ -15,6 +16,8 @@ memoryRequirements*: VkMemoryRequirements memory*: VkDeviceMemory bufferTypes*: set[BufferType] + persistentMapping: bool + mapped: pointer proc trash*(buffer: var Buffer) = assert int64(buffer.vkBuffer) != 0 @@ -37,9 +40,10 @@ physicalDevice: VkPhysicalDevice, size: uint64, bufferTypes: set[BufferType], - properties: set[VkMemoryPropertyFlagBits] + properties: set[VkMemoryPropertyFlagBits], + persistentMapping: bool = false ): Buffer = - result = Buffer(device: device, size: size, bufferTypes: bufferTypes) + result = Buffer(device: device, size: size, bufferTypes: bufferTypes, persistentMapping: persistentMapping) var usageFlags = 0 for usage in bufferTypes: usageFlags = ord(usageFlags) or ord(usage) @@ -63,13 +67,31 @@ ) checkVkResult result.device.vkAllocateMemory(addr(allocInfo), nil, addr(result.memory)) checkVkResult result.device.vkBindBufferMemory(result.vkBuffer, result.memory, VkDeviceSize(0)) + if persistentMapping: + checkVkResult vkMapMemory( + result.device, + result.memory, + offset=VkDeviceSize(0), + VkDeviceSize(result.size), + VkMemoryMapFlags(0), + addr(result.mapped) + ) template withMapping*(buffer: Buffer, data: pointer, body: untyped): untyped = + assert not buffer.persistentMapping checkVkResult vkMapMemory(buffer.device, buffer.memory, offset=VkDeviceSize(0), VkDeviceSize(buffer.size), VkMemoryMapFlags(0), addr(data)) body vkUnmapMemory(buffer.device, buffer.memory) +# note: does not work with seq +proc updateData*[T](buffer: Buffer, data: var T) = + if buffer.persistentMapping: + copyMem(buffer.mapped, addr(data), sizeof(T)) + else: + var p: pointer + buffer.withMapping(p): + copyMem(p, addr(data), sizeof(T)) proc copyBuffer*(commandPool: VkCommandPool, queue: VkQueue, src, dst: Buffer, size: uint64) = assert uint64(src.device) == uint64(dst.device) diff -r 455f1a416fe7 -r 54a1f8ee208e src/zamikongine/engine.nim --- a/src/zamikongine/engine.nim Wed Jan 11 11:55:54 2023 +0700 +++ b/src/zamikongine/engine.nim Sat Jan 14 14:08:00 2023 +0700 @@ -1,3 +1,4 @@ +import std/times import std/typetraits import std/strformat import std/enumerate @@ -13,6 +14,7 @@ import ./buffer import ./thing import ./mesh +import ./descriptor const MAX_FRAMES_IN_FLIGHT = 2 const DEBUG_LOG = not defined(release) @@ -35,10 +37,15 @@ swapchain: VkSwapchainKHR images: seq[VkImage] imageviews: seq[VkImageView] - RenderPipeline = object - shaders*: seq[ShaderProgram] + RenderPipeline[T] = object + device*: VkDevice + shaders*: seq[ShaderProgram[T]] layout*: VkPipelineLayout pipeline*: VkPipeline + uniformLayout*: VkDescriptorSetLayout + vertexBuffers*: seq[(seq[Buffer], uint32)] + indexedVertexBuffers*: seq[(seq[Buffer], Buffer, uint32, VkIndexType)] + uniformBuffers*: array[MAX_FRAMES_IN_FLIGHT, Buffer] QueueFamily = object properties*: VkQueueFamilyProperties hasSurfaceSupport*: bool @@ -51,28 +58,25 @@ formats: seq[VkSurfaceFormatKHR] presentModes: seq[VkPresentModeKHR] Vulkan* = object - debugMessenger: VkDebugUtilsMessengerEXT + debugMessenger*: VkDebugUtilsMessengerEXT instance*: VkInstance deviceList*: seq[PhysicalDevice] device*: Device surface*: VkSurfaceKHR - surfaceFormat: VkSurfaceFormatKHR - frameDimension: VkExtent2D - swapchain: Swapchain - framebuffers: seq[VkFramebuffer] + surfaceFormat*: VkSurfaceFormatKHR + frameDimension*: VkExtent2D + swapchain*: Swapchain + framebuffers*: seq[VkFramebuffer] renderPass*: VkRenderPass - pipeline*: RenderPipeline commandPool*: VkCommandPool commandBuffers*: array[MAX_FRAMES_IN_FLIGHT, VkCommandBuffer] imageAvailableSemaphores*: array[MAX_FRAMES_IN_FLIGHT, VkSemaphore] renderFinishedSemaphores*: array[MAX_FRAMES_IN_FLIGHT, VkSemaphore] inFlightFences*: array[MAX_FRAMES_IN_FLIGHT, VkFence] - vertexBuffers: seq[(seq[Buffer], uint32)] - indexedVertexBuffers: seq[(seq[Buffer], Buffer, uint32, VkIndexType)] Engine* = object - vulkan: Vulkan - window: NativeWindow - currentscenedata: ref Thing + vulkan*: Vulkan + window*: NativeWindow + currentscenedata*: ref Thing proc getAllPhysicalDevices(instance: VkInstance, surface: VkSurfaceKHR): seq[PhysicalDevice] = for vulkanPhysicalDevice in getVulkanPhysicalDevices(instance): @@ -246,10 +250,11 @@ ) checkVkResult device.vkCreateRenderPass(addr(renderPassCreateInfo), nil, addr(result)) -proc setupRenderPipeline[T](device: VkDevice, frameDimension: VkExtent2D, renderPass: VkRenderPass, vertexShader, fragmentShader: static string): RenderPipeline = +proc initRenderPipeline[VertextType, T](device: VkDevice, frameDimension: VkExtent2D, renderPass: VkRenderPass, vertexShader, fragmentShader: static string): RenderPipeline[T] = # load shaders - result.shaders.add(device.initShaderProgram(VK_SHADER_STAGE_VERTEX_BIT, vertexShader)) - result.shaders.add(device.initShaderProgram(VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShader)) + result.device = device + result.shaders.add(initShaderProgram[T](device, VK_SHADER_STAGE_VERTEX_BIT, vertexShader)) + result.shaders.add(initShaderProgram[T](device, VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShader)) var # define which parts can be dynamic (pipeline is fixed after setup) @@ -259,8 +264,8 @@ dynamicStateCount: uint32(dynamicStates.len), pDynamicStates: addr(dynamicStates[0]), ) - vertexbindings = generateInputVertexBinding[T]() - attributebindings = generateInputAttributeBinding[T]() + vertexbindings = generateInputVertexBinding[VertextType]() + attributebindings = generateInputAttributeBinding[VertextType]() # define input data format vertexInputInfo = VkPipelineVertexInputStateCreateInfo( @@ -331,11 +336,13 @@ blendConstants: [0.0'f, 0.0'f, 0.0'f, 0.0'f], ) - # create pipeline + result.uniformLayout = device.createUniformDescriptorLayout(VkShaderStageFlags(VK_SHADER_STAGE_VERTEX_BIT), 0) + var + # "globals" that go into the shader, uniforms etc. pipelineLayoutInfo = VkPipelineLayoutCreateInfo( sType: VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - setLayoutCount: 0, - pSetLayouts: nil, + setLayoutCount: 1, + pSetLayouts: addr(result.uniformLayout), pushConstantRangeCount: 0, pPushConstantRanges: nil, ) @@ -443,9 +450,9 @@ checkVkResult device.vkCreateSemaphore(addr(semaphoreInfo), nil, addr(result[1][i])) checkVkResult device.vkCreateFence(addr(fenceInfo), nil, addr(result[2][i])) -proc igniteEngine*(): Engine = +proc igniteEngine*(windowTitle: string): Engine = - result.window = createWindow("Hello triangle") + result.window = createWindow(windowTitle) # setup vulkan functions vkLoad1_0() @@ -488,30 +495,65 @@ ) = result.vulkan.device.device.setupSyncPrimitives() -proc setupPipeline*[T: object, U: uint16|uint32](engine: var Engine, scenedata: ref Thing, vertexShader, fragmentShader: static string) = +proc setupPipeline*[VertexType, UniformType, T: object, IndexType: uint16|uint32](engine: var Engine, scenedata: ref Thing, vertexShader, fragmentShader: static string): RenderPipeline[T] = engine.currentscenedata = scenedata - engine.vulkan.pipeline = setupRenderPipeline[T]( + result = initRenderPipeline[VertexType, T]( engine.vulkan.device.device, engine.vulkan.frameDimension, engine.vulkan.renderPass, vertexShader, fragmentShader, ) - var allmeshes: seq[Mesh[T]] - for mesh in partsOfType[ref Mesh[T]](engine.currentscenedata): + # vertex buffers + var allmeshes: seq[Mesh[VertexType]] + for mesh in partsOfType[ref Mesh[VertexType]](engine.currentscenedata): allmeshes.add(mesh[]) if allmeshes.len > 0: var ubermesh = createUberMesh(allmeshes) - engine.vulkan.vertexBuffers.add createVertexBuffers(ubermesh, engine.vulkan.device.device, engine.vulkan.device.physicalDevice.device, engine.vulkan.commandPool, engine.vulkan.device.graphicsQueue) + result.vertexBuffers.add createVertexBuffers(ubermesh, engine.vulkan.device.device, engine.vulkan.device.physicalDevice.device, engine.vulkan.commandPool, engine.vulkan.device.graphicsQueue) - var allindexedmeshes: seq[IndexedMesh[T, U]] - for mesh in partsOfType[ref IndexedMesh[T, U]](engine.currentscenedata): + # vertex buffers with indexes + var allindexedmeshes: seq[IndexedMesh[VertexType, IndexType]] + for mesh in partsOfType[ref IndexedMesh[VertexType, IndexType]](engine.currentscenedata): allindexedmeshes.add(mesh[]) if allindexedmeshes.len > 0: var indexedubermesh = createUberMesh(allindexedmeshes) - engine.vulkan.indexedVertexBuffers.add createIndexedVertexBuffers(indexedubermesh, engine.vulkan.device.device, engine.vulkan.device.physicalDevice.device, engine.vulkan.commandPool, engine.vulkan.device.graphicsQueue) + result.indexedVertexBuffers.add createIndexedVertexBuffers(indexedubermesh, engine.vulkan.device.device, engine.vulkan.device.physicalDevice.device, engine.vulkan.commandPool, engine.vulkan.device.graphicsQueue) + + # uniform buffers + result.uniformBuffers = createUniformBuffers[MAX_FRAMES_IN_FLIGHT, UniformType]( + engine.vulkan.device.device, + engine.vulkan.device.physicalDevice.device + ) + + +proc runPipeline(commandBuffer: VkCommandBuffer, pipeline: RenderPipeline) = + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline) -proc recordCommandBuffer(renderPass: VkRenderPass, pipeline: VkPipeline, commandBuffer: VkCommandBuffer, framebuffer: VkFramebuffer, frameDimension: VkExtent2D, engine: var Engine) = + for (vertexBufferSet, vertexCount) in pipeline.vertexBuffers: + var + vertexBuffers: seq[VkBuffer] + offsets: seq[VkDeviceSize] + for buffer in vertexBufferSet: + vertexBuffers.add buffer.vkBuffer + offsets.add VkDeviceSize(0) + + vkCmdBindVertexBuffers(commandBuffer, firstBinding=0'u32, bindingCount=2'u32, 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: + var + vertexBuffers: seq[VkBuffer] + offsets: seq[VkDeviceSize] + for buffer in vertexBufferSet: + vertexBuffers.add buffer.vkBuffer + offsets.add VkDeviceSize(0) + + vkCmdBindVertexBuffers(commandBuffer, firstBinding=0'u32, bindingCount=2'u32, pBuffers=addr(vertexBuffers[0]), pOffsets=addr(offsets[0])) + vkCmdBindIndexBuffer(commandBuffer, indexBuffer.vkBuffer, VkDeviceSize(0), indexType); + vkCmdDrawIndexed(commandBuffer, indicesCount, 1, 0, 0, 0); + +proc recordCommandBuffer(renderPass: VkRenderPass, pipeline: RenderPipeline, commandBuffer: VkCommandBuffer, framebuffer: VkFramebuffer, frameDimension: VkExtent2D) = var beginInfo = VkCommandBufferBeginInfo( sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, @@ -541,41 +583,20 @@ offset: VkOffset2D(x: 0, y: 0), extent: frameDimension ) - checkVkResult commandBuffer.vkBeginCommandBuffer(addr(beginInfo)) - commandBuffer.vkCmdBeginRenderPass(addr(renderPassInfo), VK_SUBPASS_CONTENTS_INLINE) - commandBuffer.vkCmdSetViewport(firstViewport=0, viewportCount=1, addr(viewport)) - commandBuffer.vkCmdSetScissor(firstScissor=0, scissorCount=1, addr(scissor)) - commandBuffer.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline) - - for (vertexBufferSet, vertexCount) in engine.vulkan.vertexBuffers: - var - vertexBuffers: seq[VkBuffer] - offsets: seq[VkDeviceSize] - for buffer in vertexBufferSet: - vertexBuffers.add buffer.vkBuffer - offsets.add VkDeviceSize(0) - - commandBuffer.vkCmdBindVertexBuffers(firstBinding=0'u32, bindingCount=2'u32, pBuffers=addr(vertexBuffers[0]), pOffsets=addr(offsets[0])) - commandBuffer.vkCmdDraw(vertexCount=vertexCount, instanceCount=1'u32, firstVertex=0'u32, firstInstance=0'u32) + checkVkResult vkBeginCommandBuffer(commandBuffer, addr(beginInfo)) + block: + vkCmdBeginRenderPass(commandBuffer, addr(renderPassInfo), VK_SUBPASS_CONTENTS_INLINE) + vkCmdSetViewport(commandBuffer, firstViewport=0, viewportCount=1, addr(viewport)) + vkCmdSetScissor(commandBuffer, firstScissor=0, scissorCount=1, addr(scissor)) + runPipeline(commandBuffer, pipeline) + vkCmdEndRenderPass(commandBuffer) + checkVkResult vkEndCommandBuffer(commandBuffer) - for (vertexBufferSet, indexBuffer, indicesCount, indexType) in engine.vulkan.indexedVertexBuffers: - var - vertexBuffers: seq[VkBuffer] - offsets: seq[VkDeviceSize] - for buffer in vertexBufferSet: - vertexBuffers.add buffer.vkBuffer - offsets.add VkDeviceSize(0) - - commandBuffer.vkCmdBindVertexBuffers(firstBinding=0'u32, bindingCount=2'u32, pBuffers=addr(vertexBuffers[0]), pOffsets=addr(offsets[0])) - commandBuffer.vkCmdBindIndexBuffer(indexBuffer.vkBuffer, VkDeviceSize(0), indexType); - commandBuffer.vkCmdDrawIndexed(indicesCount, 1, 0, 0, 0); - commandBuffer.vkCmdEndRenderPass() - checkVkResult commandBuffer.vkEndCommandBuffer() - -proc drawFrame(window: NativeWindow, vulkan: var Vulkan, currentFrame: int, resized: bool, engine: var Engine) = - checkVkResult vulkan.device.device.vkWaitForFences(1, addr(vulkan.inFlightFences[currentFrame]), VK_TRUE, high(uint64)) +proc drawFrame(window: NativeWindow, vulkan: var Vulkan, currentFrame: int, resized: bool, pipeline: RenderPipeline) = + checkVkResult vkWaitForFences(vulkan.device.device, 1, addr(vulkan.inFlightFences[currentFrame]), VK_TRUE, high(uint64)) var bufferImageIndex: uint32 - let nextImageResult = vulkan.device.device.vkAcquireNextImageKHR( + let nextImageResult = vkAcquireNextImageKHR( + vulkan.device.device, vulkan.swapchain.swapchain, high(uint64), vulkan.imageAvailableSemaphores[currentFrame], @@ -587,10 +608,10 @@ (vulkan.swapchain, vulkan.framebuffers) = vulkan.recreateSwapchain() elif not (nextImageResult in [VK_SUCCESS, VK_SUBOPTIMAL_KHR]): raise newException(Exception, "Vulkan error: vkAcquireNextImageKHR returned " & $nextImageResult) - checkVkResult vulkan.device.device.vkResetFences(1, addr(vulkan.inFlightFences[currentFrame])) + checkVkResult vkResetFences(vulkan.device.device, 1, addr(vulkan.inFlightFences[currentFrame])) - checkVkResult vulkan.commandBuffers[currentFrame].vkResetCommandBuffer(VkCommandBufferResetFlags(0)) - vulkan.renderPass.recordCommandBuffer(vulkan.pipeline.pipeline, vulkan.commandBuffers[currentFrame], vulkan.framebuffers[bufferImageIndex], vulkan.frameDimension, engine) + checkVkResult vkResetCommandBuffer(vulkan.commandBuffers[currentFrame], VkCommandBufferResetFlags(0)) + vulkan.renderPass.recordCommandBuffer(pipeline, vulkan.commandBuffers[currentFrame], vulkan.framebuffers[bufferImageIndex], vulkan.frameDimension) var waitSemaphores = [vulkan.imageAvailableSemaphores[currentFrame]] waitStages = [VkPipelineStageFlags(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT)] @@ -623,13 +644,16 @@ (vulkan.swapchain, vulkan.framebuffers) = vulkan.recreateSwapchain() -proc fullThrottle*(engine: var Engine) = +proc run*(engine: var Engine, pipeline: RenderPipeline, globalUpdate: proc(engine: var Engine, dt: Duration)) = var killed = false currentFrame = 0 resized = false + lastUpdate = getTime() while not killed: + + # process input for event in engine.window.pendingEvents(): case event.eventType: of Quit: @@ -642,38 +666,54 @@ killed = true else: discard - engine.window.drawFrame(engine.vulkan, currentFrame, resized, engine) + + # game logic update + let + now = getTime() + dt = now - lastUpdate + lastUpdate = now + engine.globalUpdate(dt) + for entity in allEntities(engine.currentscenedata): + entity.update(dt) + + # submit frame for drawing + engine.window.drawFrame(engine.vulkan, currentFrame, resized, pipeline) resized = false currentFrame = (currentFrame + 1) mod MAX_FRAMES_IN_FLIGHT; - checkVkResult engine.vulkan.device.device.vkDeviceWaitIdle() + checkVkResult vkDeviceWaitIdle(engine.vulkan.device.device) -proc trash*(engine: var Engine) = - for (bufferset, cnt) in engine.vulkan.vertexBuffers.mitems: +proc trash*(pipeline: var RenderPipeline) = + vkDestroyDescriptorSetLayout(pipeline.device, pipeline.uniformLayout, nil); + vkDestroyPipeline(pipeline.device, pipeline.pipeline, nil) + vkDestroyPipelineLayout(pipeline.device, pipeline.layout, nil) + for shader in pipeline.shaders: + vkDestroyShaderModule(pipeline.device, shader.shader.module, nil) + + for (bufferset, cnt) in pipeline.vertexBuffers.mitems: for buffer in bufferset.mitems: buffer.trash() - for (bufferset, indexbuffer, cnt, t) in engine.vulkan.indexedVertexBuffers.mitems: + for (bufferset, indexbuffer, cnt, t) in pipeline.indexedVertexBuffers.mitems: indexbuffer.trash() for buffer in bufferset.mitems: buffer.trash() + for buffer in pipeline.uniformBuffers.mitems: + buffer.trash() + +proc trash*(engine: var Engine) = + checkVkResult vkDeviceWaitIdle(engine.vulkan.device.device) engine.vulkan.device.device.trash(engine.vulkan.swapchain, engine.vulkan.framebuffers) - checkVkResult engine.vulkan.device.device.vkDeviceWaitIdle() for i in 0 ..< MAX_FRAMES_IN_FLIGHT: engine.vulkan.device.device.vkDestroySemaphore(engine.vulkan.imageAvailableSemaphores[i], nil) engine.vulkan.device.device.vkDestroySemaphore(engine.vulkan.renderFinishedSemaphores[i], nil) engine.vulkan.device.device.vkDestroyFence(engine.vulkan.inFlightFences[i], nil) + engine.vulkan.device.device.vkDestroyRenderPass(engine.vulkan.renderPass, nil) engine.vulkan.device.device.vkDestroyCommandPool(engine.vulkan.commandPool, nil) - engine.vulkan.device.device.vkDestroyPipeline(engine.vulkan.pipeline.pipeline, nil) - engine.vulkan.device.device.vkDestroyPipelineLayout(engine.vulkan.pipeline.layout, nil) - engine.vulkan.device.device.vkDestroyRenderPass(engine.vulkan.renderPass, nil) - - for shader in engine.vulkan.pipeline.shaders: - engine.vulkan.device.device.vkDestroyShaderModule(shader.shader.module, nil) engine.vulkan.instance.vkDestroySurfaceKHR(engine.vulkan.surface, nil) engine.vulkan.device.device.vkDestroyDevice(nil) when DEBUG_LOG: engine.vulkan.instance.vkDestroyDebugUtilsMessengerEXT(engine.vulkan.debugMessenger, nil) engine.window.trash() - engine.vulkan.instance.vkDestroyInstance(nil) + engine.vulkan.instance.vkDestroyInstance(nil) # needs to happen after window is trashed as the driver might have a hook registered for the window destruction diff -r 455f1a416fe7 -r 54a1f8ee208e src/zamikongine/shader.nim --- a/src/zamikongine/shader.nim Wed Jan 11 11:55:54 2023 +0700 +++ b/src/zamikongine/shader.nim Sat Jan 14 14:08:00 2023 +0700 @@ -7,12 +7,16 @@ import ./vulkan_helpers import ./vulkan import ./vertex +import ./math/vector type - ShaderProgram* = object + AllowedUniformType = SomeNumber|Vec + UniformSlot *[T:AllowedUniformType] = object + ShaderProgram*[Uniforms] = object entryPoint*: string programType*: VkShaderStageFlagBits shader*: VkPipelineShaderStageCreateInfo + uniforms*: Uniforms func stage2string(stage: VkShaderStageFlagBits): string {.compileTime.} = case stage @@ -26,9 +30,11 @@ of VK_SHADER_STAGE_ALL: "" proc compileGLSLToSPIRV(stage: VkShaderStageFlagBits, shaderSource: string, entrypoint: string): seq[uint32] {.compileTime.} = - # TODO: compiles only on linux for now (because we don't have compile-time functionality in std/tempfile) 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) @@ -52,7 +58,7 @@ ) i += 4 -proc initShaderProgram*(device: VkDevice, programType: static VkShaderStageFlagBits, shader: static string, entryPoint: static string="main"): ShaderProgram = +proc initShaderProgram*[T](device: VkDevice, programType: static VkShaderStageFlagBits, shader: static string, entryPoint: static string="main"): ShaderProgram[T] = result.entryPoint = entryPoint result.programType = programType @@ -73,22 +79,25 @@ pName: cstring(result.entryPoint), # entry point for shader ) -func generateVertexShaderCode*[T](entryPoint, positionAttrName, colorAttrName: static string): string {.compileTime.} = +func generateVertexShaderCode*[VertexType](entryPoint, positionAttrName, colorAttrName: static string): string {.compileTime.} = var lines: seq[string] lines.add "#version 450" - lines.add generateGLSLDeclarations[T]() + # lines.add "layout(binding = 0) uniform UniformBufferObject { float dt; } ubo;" + lines.add generateGLSLDeclarations[VertexType]() lines.add "layout(location = 0) out vec3 fragColor;" lines.add "void " & entryPoint & "() {" - for name, value in T().fieldPairs: + for name, value in VertexType().fieldPairs: when typeof(value) is VertexAttribute and name == positionAttrName: - lines.add " gl_Position = vec4(" & name & ", 0.0, 1.0);" + # lines.add " vec2 tmp = " & name & " * ubo.dt;" + lines.add " vec2 tmp = " & name & ";" + lines.add " gl_Position = vec4(tmp, 0.0, 1.0);" when typeof(value) is VertexAttribute and name == colorAttrName: lines.add " fragColor = " & name & ";" lines.add "}" return lines.join("\n") -func generateFragmentShaderCode*[T](entryPoint: static string): string {.compileTime.} = +func generateFragmentShaderCode*[VertexType](entryPoint: static string): string {.compileTime.} = var lines: seq[string] lines.add "#version 450" lines.add "layout(location = 0) in vec3 fragColor;" diff -r 455f1a416fe7 -r 54a1f8ee208e src/zamikongine/thing.nim --- a/src/zamikongine/thing.nim Wed Jan 11 11:55:54 2023 +0700 +++ b/src/zamikongine/thing.nim Sat Jan 14 14:08:00 2023 +0700 @@ -1,4 +1,5 @@ {.experimental: "codeReordering".} +import std/times type Part* = object of RootObj @@ -9,6 +10,8 @@ children*: seq[ref Thing] parts*: seq[ref Part] +method update*(thing: ref Thing, dt: Duration) {.base.} = discard + iterator partsOfType*[T: ref Part](root: ref Thing): T = var queue = @[root] while queue.len > 0: @@ -18,3 +21,11 @@ yield T(part) for child in thing.children: queue.insert(child, 0) + +iterator allEntities*(root: ref Thing): ref Thing = + var queue = @[root] + while queue.len > 0: + let next = queue.pop + for child in next.children: + queue.add child + yield next