# HG changeset patch # User sam # Date 1711805373 -25200 # Node ID 925197a1ec9559d1b2936cae2447d79a2c47f203 # Parent 8526d1da2f693e00342a9b33e2809b9b5009e45d add: some sync (not finished), simplify renderpass af diff -r 8526d1da2f69 -r 925197a1ec95 semicongine/core/utils.nim --- a/semicongine/core/utils.nim Fri Mar 29 16:28:30 2024 +0700 +++ b/semicongine/core/utils.nim Sat Mar 30 20:29:33 2024 +0700 @@ -18,7 +18,7 @@ result = join(str[0 ..< i]) break -func toCPointer*[T](list: seq[T]): ptr T = +func toCPointer*[T](list: openArray[T]): ptr T = if list.len > 0: addr(list[0]) else: nil proc staticExecChecked*(command: string, input = ""): string {.compileTime.} = diff -r 8526d1da2f69 -r 925197a1ec95 semicongine/core/vulkanapi.nim --- a/semicongine/core/vulkanapi.nim Fri Mar 29 16:28:30 2024 +0700 +++ b/semicongine/core/vulkanapi.nim Sat Mar 30 20:29:33 2024 +0700 @@ -402,13 +402,13 @@ VkShaderCorePropertiesFlagsAMD* = distinct VkFlags VkDeviceDiagnosticsConfigFlagsNV* = distinct VkFlags VkRefreshObjectFlagsKHR* = distinct VkFlags - VkAccessFlags2* = distinct VkFlags - VkPipelineStageFlags2* = distinct VkFlags + VkAccessFlags2* = distinct VkFlags64 + VkPipelineStageFlags2* = distinct VkFlags64 VkAccelerationStructureMotionInfoFlagsNV* = distinct VkFlags VkAccelerationStructureMotionInstanceFlagsNV* = distinct VkFlags - VkFormatFeatureFlags2* = distinct VkFlags + VkFormatFeatureFlags2* = distinct VkFlags64 VkRenderingFlags* = distinct VkFlags - VkMemoryDecompressionMethodFlagsNV* = distinct VkFlags + VkMemoryDecompressionMethodFlagsNV* = distinct VkFlags64 VkBuildMicromapFlagsEXT* = distinct VkFlags VkMicromapCreateFlagsEXT* = distinct VkFlags VkDirectDriverLoadingFlagsLUNARG* = distinct VkFlags diff -r 8526d1da2f69 -r 925197a1ec95 semicongine/engine.nim --- a/semicongine/engine.nim Fri Mar 29 16:28:30 2024 +0700 +++ b/semicongine/engine.nim Sat Mar 30 20:29:33 2024 +0700 @@ -217,7 +217,6 @@ func mouseMove*(engine: Engine): auto = engine.input.mouseMove func mouseWheel*(engine: Engine): auto = engine.input.mouseWheel func eventsProcessed*(engine: Engine): auto = engine.input.eventsProcessed -func framesRendered*(engine: Engine): uint64 = (if engine.renderer.isSome: engine.renderer.get.framesRendered else: 0) func gpuDevice*(engine: Engine): Device = engine.device func getWindow*(engine: Engine): auto = engine.window func getAspectRatio*(engine: Engine): float32 = engine.getWindow().size[0] / engine.getWindow().size[1] diff -r 8526d1da2f69 -r 925197a1ec95 semicongine/renderer.nim --- a/semicongine/renderer.nim Fri Mar 29 16:28:30 2024 +0700 +++ b/semicongine/renderer.nim Sat Mar 30 20:29:33 2024 +0700 @@ -6,6 +6,7 @@ import std/logging import ./core +import ./vulkan/commandbuffer import ./vulkan/buffer import ./vulkan/device import ./vulkan/drawable @@ -37,40 +38,46 @@ materials: Table[MaterialType, seq[MaterialData]] Renderer* = object device: Device - surfaceFormat: VkSurfaceFormatKHR renderPass: RenderPass swapchain: Swapchain scenedata: Table[Scene, SceneData] emptyTexture: VulkanTexture + queue: Queue + commandBufferPool: CommandBufferPool + +proc currentCommandBuffer(renderer: Renderer): VkCommandBuffer = + renderer.commandBufferPool.buffers[renderer.swapchain.currentInFlight] proc initRenderer*(device: Device, shaders: openArray[(MaterialType, ShaderConfiguration)], clearColor = Vec4f([0.8'f32, 0.8'f32, 0.8'f32, 1'f32]), backFaceCulling = true): Renderer = assert device.vk.valid result.device = device - result.renderPass = device.simpleForwardRenderPass(shaders, clearColor = clearColor, backFaceCulling = backFaceCulling) - result.surfaceFormat = device.physicalDevice.getSurfaceFormats().filterSurfaceFormat() - # use last renderpass as output for swapchain - let swapchain = device.createSwapchain(result.renderPass.vk, result.surfaceFormat, device.firstGraphicsQueue().get().family) + result.renderPass = device.createRenderPass(shaders, clearColor = clearColor, backFaceCulling = backFaceCulling) + let swapchain = device.createSwapchain( + result.renderPass.vk, + device.physicalDevice.getSurfaceFormats().filterSurfaceFormat(), + ) if not swapchain.isSome: raise newException(Exception, "Unable to create swapchain") + result.queue = device.firstGraphicsQueue().get() + result.commandBufferPool = device.createCommandBufferPool(result.queue.family, swapchain.get().inFlightFrames) result.swapchain = swapchain.get() - result.emptyTexture = device.uploadTexture(EMPTYTEXTURE) + result.emptyTexture = device.uploadTexture(result.queue, EMPTYTEXTURE) func inputs(renderer: Renderer, scene: Scene): seq[ShaderAttribute] = var found: Table[string, ShaderAttribute] - for i in 0 ..< renderer.renderPass.subpasses.len: - for (materialType, shaderPipeline) in renderer.renderPass.subpasses[i].shaderPipelines: - if scene.usesMaterial(materialType): - for input in shaderPipeline.inputs: - if found.contains(input.name): - assert input.name == found[input.name].name, &"{input.name}: {input.name} != {found[input.name].name}" - assert input.theType == found[input.name].theType, &"{input.name}: {input.theType} != {found[input.name].theType}" - assert input.arrayCount == found[input.name].arrayCount, &"{input.name}: {input.arrayCount} != {found[input.name].arrayCount}" - assert input.memoryPerformanceHint == found[input.name].memoryPerformanceHint, &"{input.name}: {input.memoryPerformanceHint} != {found[input.name].memoryPerformanceHint}" - else: - result.add input - found[input.name] = input + for (materialType, shaderPipeline) in renderer.renderPass.shaderPipelines: + if scene.usesMaterial(materialType): + for input in shaderPipeline.inputs: + if found.contains(input.name): + assert input.name == found[input.name].name, &"{input.name}: {input.name} != {found[input.name].name}" + assert input.theType == found[input.name].theType, &"{input.name}: {input.theType} != {found[input.name].theType}" + assert input.arrayCount == found[input.name].arrayCount, &"{input.name}: {input.arrayCount} != {found[input.name].arrayCount}" + assert input.memoryPerformanceHint == found[input.name].memoryPerformanceHint, &"{input.name}: {input.memoryPerformanceHint} != {found[input.name].memoryPerformanceHint}" + else: + result.add input + found[input.name] = input proc materialCompatibleWithPipeline(scene: Scene, materialType: MaterialType, shaderPipeline: ShaderPipeline): (bool, string) = for uniform in shaderPipeline.uniforms: @@ -116,14 +123,13 @@ var foundRenderableObject = false var materialTypes: seq[MaterialType] - for i in 0 ..< renderer.renderPass.subpasses.len: - for (materialType, shaderPipeline) in renderer.renderPass.subpasses[i].shaderPipelines: - materialTypes.add materialType - for mesh in scene.meshes: - if mesh.material.theType == materialType: - foundRenderableObject = true - let (error, message) = scene.meshCompatibleWithPipeline(mesh, shaderPipeline) - assert not error, &"Mesh '{mesh}' not compatible with assigned shaderPipeline ({materialType}) because: {message}" + for (materialType, shaderPipeline) in renderer.renderPass.shaderPipelines: + materialTypes.add materialType + for mesh in scene.meshes: + if mesh.material.theType == materialType: + foundRenderableObject = true + let (error, message) = scene.meshCompatibleWithPipeline(mesh, shaderPipeline) + assert not error, &"Mesh '{mesh}' not compatible with assigned shaderPipeline ({materialType}) because: {message}" if not foundRenderableObject: var matTypes: Table[string, MaterialType] @@ -218,12 +224,11 @@ # fill offsets per shaderPipeline (as sequence corresponds to shader input binding) var offsets: Table[VkPipeline, seq[(string, MemoryPerformanceHint, int)]] - for subpass_i in 0 ..< renderer.renderPass.subpasses.len: - for (materialType, shaderPipeline) in renderer.renderPass.subpasses[subpass_i].shaderPipelines: - if scene.usesMaterial(materialType): - offsets[shaderPipeline.vk] = newSeq[(string, MemoryPerformanceHint, int)]() - for attribute in shaderPipeline.inputs: - offsets[shaderPipeline.vk].add (attribute.name, attribute.memoryPerformanceHint, scenedata.vertexBufferOffsets[(mesh, attribute.name)]) + for (materialType, shaderPipeline) in renderer.renderPass.shaderPipelines: + if scene.usesMaterial(materialType): + offsets[shaderPipeline.vk] = newSeq[(string, MemoryPerformanceHint, int)]() + for attribute in shaderPipeline.inputs: + offsets[shaderPipeline.vk].add (attribute.name, attribute.memoryPerformanceHint, scenedata.vertexBufferOffsets[(mesh, attribute.name)]) # create drawables let indexed = mesh.indexType != MeshIndexType.None @@ -246,73 +251,72 @@ drawable.indexBufferOffset = indexBufferOffset drawable.indexType = mesh.indexType var (pdata, size) = mesh[].getRawIndexData() - scenedata.indexBuffer.setData(pdata, size, indexBufferOffset) + scenedata.indexBuffer.setData(renderer.queue, pdata, size, indexBufferOffset) indexBufferOffset += size scenedata.drawables.add (drawable, mesh) # setup uniforms and textures (anything descriptor) var uploadedTextures: Table[Texture, VulkanTexture] - for subpass_i in 0 ..< renderer.renderPass.subpasses.len: - for (materialType, shaderPipeline) in renderer.renderPass.subpasses[subpass_i].shaderPipelines: - if scene.usesMaterial(materialType): - # gather textures - scenedata.textures[shaderPipeline.vk] = initTable[string, seq[VulkanTexture]]() - for texture in shaderPipeline.samplers: - scenedata.textures[shaderPipeline.vk][texture.name] = newSeq[VulkanTexture]() - if scene.shaderGlobals.contains(texture.name): - for textureValue in scene.shaderGlobals[texture.name][Texture][]: - if not uploadedTextures.contains(textureValue): - uploadedTextures[textureValue] = renderer.device.uploadTexture(textureValue) - scenedata.textures[shaderPipeline.vk][texture.name].add uploadedTextures[textureValue] - else: - var foundTexture = false - for material in scene.getMaterials(materialType): - if material.hasMatchingAttribute(texture): - foundTexture = true - let value = material[texture.name, Texture][] - assert value.len == 1, &"Mesh material attribute '{texture.name}' has texture-array, but only single textures are allowed" - if not uploadedTextures.contains(value[0]): - uploadedTextures[value[0]] = renderer.device.uploadTexture(value[0]) - scenedata.textures[shaderPipeline.vk][texture.name].add uploadedTextures[value[0]] - assert foundTexture, &"No texture found in shaderGlobals or materials for '{texture.name}'" - let nTextures = scenedata.textures[shaderPipeline.vk][texture.name].len - assert (texture.arrayCount == 0 and nTextures == 1) or texture.arrayCount >= nTextures, &"Shader assigned to render '{materialType}' expected {texture.arrayCount} textures for '{texture.name}' but got {nTextures}" - if texture.arrayCount < nTextures: - warn &"Shader assigned to render '{materialType}' expected {texture.arrayCount} textures for '{texture.name}' but got {nTextures}" + for (materialType, shaderPipeline) in renderer.renderPass.shaderPipelines: + if scene.usesMaterial(materialType): + # gather textures + scenedata.textures[shaderPipeline.vk] = initTable[string, seq[VulkanTexture]]() + for texture in shaderPipeline.samplers: + scenedata.textures[shaderPipeline.vk][texture.name] = newSeq[VulkanTexture]() + if scene.shaderGlobals.contains(texture.name): + for textureValue in scene.shaderGlobals[texture.name][Texture][]: + if not uploadedTextures.contains(textureValue): + uploadedTextures[textureValue] = renderer.device.uploadTexture(renderer.queue, textureValue) + scenedata.textures[shaderPipeline.vk][texture.name].add uploadedTextures[textureValue] + else: + var foundTexture = false + for material in scene.getMaterials(materialType): + if material.hasMatchingAttribute(texture): + foundTexture = true + let value = material[texture.name, Texture][] + assert value.len == 1, &"Mesh material attribute '{texture.name}' has texture-array, but only single textures are allowed" + if not uploadedTextures.contains(value[0]): + uploadedTextures[value[0]] = renderer.device.uploadTexture(renderer.queue, value[0]) + scenedata.textures[shaderPipeline.vk][texture.name].add uploadedTextures[value[0]] + assert foundTexture, &"No texture found in shaderGlobals or materials for '{texture.name}'" + let nTextures = scenedata.textures[shaderPipeline.vk][texture.name].len + assert (texture.arrayCount == 0 and nTextures == 1) or texture.arrayCount >= nTextures, &"Shader assigned to render '{materialType}' expected {texture.arrayCount} textures for '{texture.name}' but got {nTextures}" + if texture.arrayCount < nTextures: + warn &"Shader assigned to render '{materialType}' expected {texture.arrayCount} textures for '{texture.name}' but got {nTextures}" - # gather uniform sizes - var uniformBufferSize = 0 - for uniform in shaderPipeline.uniforms: - uniformBufferSize += uniform.size - if uniformBufferSize > 0: - scenedata.uniformBuffers[shaderPipeline.vk] = newSeq[Buffer]() - for frame_i in 0 ..< renderer.swapchain.inFlightFrames: - scenedata.uniformBuffers[shaderPipeline.vk].add renderer.device.createBuffer( - size = uniformBufferSize, - usage = [VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT], - requireMappable = true, - preferVRAM = true, - ) + # gather uniform sizes + var uniformBufferSize = 0 + for uniform in shaderPipeline.uniforms: + uniformBufferSize += uniform.size + if uniformBufferSize > 0: + scenedata.uniformBuffers[shaderPipeline.vk] = newSeq[Buffer]() + for frame_i in 0 ..< renderer.swapchain.inFlightFrames: + scenedata.uniformBuffers[shaderPipeline.vk].add renderer.device.createBuffer( + size = uniformBufferSize, + usage = [VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT], + requireMappable = true, + preferVRAM = true, + ) - # TODO: rework the whole descriptor/pool/layout stuff, a bit unclear - var poolsizes = @[(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, renderer.swapchain.inFlightFrames)] - var nTextures = 0 - for descriptor in shaderPipeline.descriptorSetLayout.descriptors: - if descriptor.thetype == ImageSampler: - nTextures += descriptor.count - if nTextures > 0: - poolsizes.add (VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, nTextures * renderer.swapchain.inFlightFrames) - scenedata.descriptorPools[shaderPipeline.vk] = renderer.device.createDescriptorSetPool(poolsizes) + # TODO: rework the whole descriptor/pool/layout stuff, a bit unclear + var poolsizes = @[(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, renderer.swapchain.inFlightFrames)] + var nTextures = 0 + for descriptor in shaderPipeline.descriptorSetLayout.descriptors: + if descriptor.thetype == ImageSampler: + nTextures += descriptor.count + if nTextures > 0: + poolsizes.add (VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, nTextures * renderer.swapchain.inFlightFrames) + scenedata.descriptorPools[shaderPipeline.vk] = renderer.device.createDescriptorSetPool(poolsizes) - scenedata.descriptorSets[shaderPipeline.vk] = shaderPipeline.setupDescriptors( - scenedata.descriptorPools[shaderPipeline.vk], - scenedata.uniformBuffers.getOrDefault(shaderPipeline.vk, @[]), - scenedata.textures[shaderPipeline.vk], - inFlightFrames = renderer.swapchain.inFlightFrames, - emptyTexture = renderer.emptyTexture, - ) - for frame_i in 0 ..< renderer.swapchain.inFlightFrames: - scenedata.descriptorSets[shaderPipeline.vk][frame_i].writeDescriptorSet() + scenedata.descriptorSets[shaderPipeline.vk] = shaderPipeline.setupDescriptors( + scenedata.descriptorPools[shaderPipeline.vk], + scenedata.uniformBuffers.getOrDefault(shaderPipeline.vk, @[]), + scenedata.textures[shaderPipeline.vk], + inFlightFrames = renderer.swapchain.inFlightFrames, + emptyTexture = renderer.emptyTexture, + ) + for frame_i in 0 ..< renderer.swapchain.inFlightFrames: + scenedata.descriptorSets[shaderPipeline.vk][frame_i].writeDescriptorSet() renderer.scenedata[scene] = scenedata @@ -324,6 +328,7 @@ let memoryPerformanceHint = renderer.scenedata[scene].attributeLocation[attribute] renderer.scenedata[scene].vertexBuffers[memoryPerformanceHint].setData( + renderer.queue, mesh[].getPointer(attribute), mesh[].attributeSize(attribute), renderer.scenedata[scene].vertexBufferOffsets[(mesh, attribute)] @@ -352,60 +357,57 @@ debug &"Update uniforms because of dirty scene globals: {dirty}" # loop over all used shaders/pipelines - for i in 0 ..< renderer.renderPass.subpasses.len: - for (materialType, shaderPipeline) in renderer.renderPass.subpasses[i].shaderPipelines: - if ( - scene.usesMaterial(materialType) and - renderer.scenedata[scene].uniformBuffers.hasKey(shaderPipeline.vk) and - renderer.scenedata[scene].uniformBuffers[shaderPipeline.vk].len != 0 - ): - var dirtyMaterialAttribs: seq[string] - for material in renderer.scenedata[scene].materials[materialType].mitems: - dirtyMaterialAttribs.add material.dirtyAttributes - material.clearDirtyAttributes() - assert renderer.scenedata[scene].uniformBuffers[shaderPipeline.vk][renderer.swapchain.currentInFlight].vk.valid - if forceAll: - for buffer in renderer.scenedata[scene].uniformBuffers[shaderPipeline.vk]: - assert buffer.vk.valid + for (materialType, shaderPipeline) in renderer.renderPass.shaderPipelines: + if ( + scene.usesMaterial(materialType) and + renderer.scenedata[scene].uniformBuffers.hasKey(shaderPipeline.vk) and + renderer.scenedata[scene].uniformBuffers[shaderPipeline.vk].len != 0 + ): + var dirtyMaterialAttribs: seq[string] + for material in renderer.scenedata[scene].materials[materialType].mitems: + dirtyMaterialAttribs.add material.dirtyAttributes + material.clearDirtyAttributes() + assert renderer.scenedata[scene].uniformBuffers[shaderPipeline.vk][renderer.swapchain.currentInFlight].vk.valid + if forceAll: + for buffer in renderer.scenedata[scene].uniformBuffers[shaderPipeline.vk]: + assert buffer.vk.valid - var offset = 0 - # loop over all uniforms of the shader-shaderPipeline - for uniform in shaderPipeline.uniforms: - if dirty.contains(uniform.name) or dirtyMaterialAttribs.contains(uniform.name) or forceAll: # only update uniforms if necessary - var value = initDataList(uniform.theType) - if scene.shaderGlobals.hasKey(uniform.name): - assert scene.shaderGlobals[uniform.name].thetype == uniform.thetype - value = scene.shaderGlobals[uniform.name] - else: - var foundValue = false - for material in renderer.scenedata[scene].materials[materialType]: - if material.hasMatchingAttribute(uniform): - value.appendValues(material[uniform.name]) - foundValue = true - assert foundValue, &"Uniform '{uniform.name}' not found in scene shaderGlobals or materials" - assert (uniform.arrayCount == 0 and value.len == 1) or value.len <= uniform.arrayCount, &"Uniform '{uniform.name}' found has wrong length (shader declares {uniform.arrayCount} but shaderGlobals and materials provide {value.len})" - if value.len <= uniform.arrayCount: - debug &"Uniform '{uniform.name}' found has short length (shader declares {uniform.arrayCount} but shaderGlobals and materials provide {value.len})" - assert value.size <= uniform.size, &"During uniform update: gathered value has size {value.size} but uniform expects size {uniform.size}" - if value.size < uniform.size: - debug &"During uniform update: gathered value has size {value.size} but uniform expects size {uniform.size}" - debug &" update uniform '{uniform.name}' with value: {value}" - # TODO: technically we would only need to update the uniform buffer of the current - # frameInFlight (I think), but we don't track for which frame the shaderglobals are no longer dirty - # therefore we have to update the uniform values in all buffers, of all inFlightframes (usually 2) - for buffer in renderer.scenedata[scene].uniformBuffers[shaderPipeline.vk]: - buffer.setData(value.getPointer(), value.size, offset) - offset += uniform.size + var offset = 0 + # loop over all uniforms of the shader-shaderPipeline + for uniform in shaderPipeline.uniforms: + if dirty.contains(uniform.name) or dirtyMaterialAttribs.contains(uniform.name) or forceAll: # only update uniforms if necessary + var value = initDataList(uniform.theType) + if scene.shaderGlobals.hasKey(uniform.name): + assert scene.shaderGlobals[uniform.name].thetype == uniform.thetype + value = scene.shaderGlobals[uniform.name] + else: + var foundValue = false + for material in renderer.scenedata[scene].materials[materialType]: + if material.hasMatchingAttribute(uniform): + value.appendValues(material[uniform.name]) + foundValue = true + assert foundValue, &"Uniform '{uniform.name}' not found in scene shaderGlobals or materials" + assert (uniform.arrayCount == 0 and value.len == 1) or value.len <= uniform.arrayCount, &"Uniform '{uniform.name}' found has wrong length (shader declares {uniform.arrayCount} but shaderGlobals and materials provide {value.len})" + if value.len <= uniform.arrayCount: + debug &"Uniform '{uniform.name}' found has short length (shader declares {uniform.arrayCount} but shaderGlobals and materials provide {value.len})" + assert value.size <= uniform.size, &"During uniform update: gathered value has size {value.size} but uniform expects size {uniform.size}" + if value.size < uniform.size: + debug &"During uniform update: gathered value has size {value.size} but uniform expects size {uniform.size}" + debug &" update uniform '{uniform.name}' with value: {value}" + # TODO: technically we would only need to update the uniform buffer of the current + # frameInFlight (I think), but we don't track for which frame the shaderglobals are no longer dirty + # therefore we have to update the uniform values in all buffers, of all inFlightframes (usually 2) + for buffer in renderer.scenedata[scene].uniformBuffers[shaderPipeline.vk]: + buffer.setData(renderer.queue, value.getPointer(), value.size, offset) + offset += uniform.size scene.clearDirtyShaderGlobals() proc render*(renderer: var Renderer, scene: Scene) = assert scene in renderer.scenedata - var - commandBufferResult = renderer.swapchain.nextFrame() - commandBuffer: VkCommandBuffer + var currentInFlightFrameIndex = renderer.swapchain.nextFrame() - if not commandBufferResult.isSome: + if not currentInFlightFrameIndex.isSome: let res = renderer.swapchain.recreate() if res.isSome: var oldSwapchain = renderer.swapchain @@ -414,29 +416,24 @@ oldSwapchain.destroy() return - commandBuffer = commandBufferResult.get() - commandBuffer.beginRenderCommands(renderer.renderPass, renderer.swapchain.currentFramebuffer(), oneTimeSubmit = true) + renderer.currentCommandBuffer.beginRenderCommands(renderer.renderPass, renderer.swapchain.currentFramebuffer(), oneTimeSubmit = true) debug "Scene buffers:" for (location, buffer) in renderer.scenedata[scene].vertexBuffers.pairs: debug " ", location, ": ", buffer debug " Index buffer: ", renderer.scenedata[scene].indexBuffer - for i in 0 ..< renderer.renderPass.subpasses.len: - for (materialType, shaderPipeline) in renderer.renderPass.subpasses[i].shaderPipelines: - if scene.usesMaterial(materialType): - debug &"Start shaderPipeline for '{materialType}'" - commandBuffer.vkCmdBindPipeline(renderer.renderPass.subpasses[i].pipelineBindPoint, shaderPipeline.vk) - commandBuffer.vkCmdBindDescriptorSets(renderer.renderPass.subpasses[i].pipelineBindPoint, shaderPipeline.layout, 0, 1, addr(renderer.scenedata[scene].descriptorSets[shaderPipeline.vk][renderer.swapchain.currentInFlight].vk), 0, nil) - for (drawable, mesh) in renderer.scenedata[scene].drawables.filterIt(it[1].visible and it[1].material.theType == materialType): - drawable.draw(commandBuffer, vertexBuffers = renderer.scenedata[scene].vertexBuffers, indexBuffer = renderer.scenedata[scene].indexBuffer, shaderPipeline.vk) + for (materialType, shaderPipeline) in renderer.renderPass.shaderPipelines: + if scene.usesMaterial(materialType): + debug &"Start shaderPipeline for '{materialType}'" + renderer.currentCommandBuffer.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, shaderPipeline.vk) + renderer.currentCommandBuffer.vkCmdBindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, shaderPipeline.layout, 0, 1, addr(renderer.scenedata[scene].descriptorSets[shaderPipeline.vk][renderer.swapchain.currentInFlight].vk), 0, nil) + for (drawable, mesh) in renderer.scenedata[scene].drawables.filterIt(it[1].visible and it[1].material.theType == materialType): + drawable.draw(renderer.currentCommandBuffer, vertexBuffers = renderer.scenedata[scene].vertexBuffers, indexBuffer = renderer.scenedata[scene].indexBuffer, shaderPipeline.vk) - if i < renderer.renderPass.subpasses.len - 1: - commandBuffer.vkCmdNextSubpass(VK_SUBPASS_CONTENTS_INLINE) + renderer.currentCommandBuffer.endRenderCommands() - commandBuffer.endRenderCommands() - - if not renderer.swapchain.swap(): + if not renderer.swapchain.swap(renderer.currentCommandBuffer): let res = renderer.swapchain.recreate() if res.isSome: var oldSwapchain = renderer.swapchain @@ -444,9 +441,6 @@ checkVkResult renderer.device.vk.vkDeviceWaitIdle() oldSwapchain.destroy() -func framesRendered*(renderer: Renderer): uint64 = - renderer.swapchain.framesRendered - func valid*(renderer: Renderer): bool = renderer.device.vk.valid @@ -476,6 +470,7 @@ proc destroy*(renderer: var Renderer) = checkVkResult renderer.device.vk.vkDeviceWaitIdle() + for scenedata in renderer.scenedata.mvalues: for buffer in scenedata.vertexBuffers.mvalues: assert buffer.vk.valid @@ -500,4 +495,5 @@ renderer.scenedata.del(scene) renderer.emptyTexture.destroy() renderer.renderPass.destroy() + renderer.commandBufferPool.destroy() renderer.swapchain.destroy() diff -r 8526d1da2f69 -r 925197a1ec95 semicongine/vulkan/buffer.nim --- a/semicongine/vulkan/buffer.nim Fri Mar 29 16:28:30 2024 +0700 +++ b/semicongine/vulkan/buffer.nim Sat Mar 30 20:29:33 2024 +0700 @@ -98,7 +98,7 @@ result.allocateMemory(requireMappable = requireMappable, preferVRAM = preferVRAM, preferAutoFlush = preferAutoFlush) -proc copy*(src, dst: Buffer, dstOffset = 0) = +proc copy*(src, dst: Buffer, queue: Queue, dstOffset = 0) = assert src.device.vk.valid assert dst.device.vk.valid assert src.device == dst.device @@ -107,7 +107,15 @@ assert VK_BUFFER_USAGE_TRANSFER_DST_BIT in dst.usage var copyRegion = VkBufferCopy(size: VkDeviceSize(src.size), dstOffset: VkDeviceSize(dstOffset)) - withSingleUseCommandBuffer(src.device, true, commandBuffer): + withSingleUseCommandBuffer(src.device, queue, true, commandBuffer): + let barrier = VkMemoryBarrier2( + sType: VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + srcStageMask: [VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT].toBits, + srcAccessMask: [VK_ACCESS_2_MEMORY_WRITE_BIT].toBits, + dstStageMask: [VK_PIPELINE_STAGE_2_VERTEX_ATTRIBUTE_INPUT_BIT].toBits, + dstAccessMask: [VK_ACCESS_2_MEMORY_READ_BIT].toBits, + ) + commandBuffer.pipelineBarrier(memoryBarriers = [barrier]) commandBuffer.vkCmdCopyBuffer(src.vk, dst.vk, 1, addr(copyRegion)) proc destroy*(buffer: var Buffer) = @@ -126,7 +134,7 @@ ) buffer.vk.reset -proc setData*(dst: Buffer, src: pointer, size: int, bufferOffset = 0) = +proc setData*(dst: Buffer, queue: Queue, src: pointer, size: int, bufferOffset = 0) = assert bufferOffset + size <= dst.size if dst.memory.canMap: copyMem(cast[pointer](cast[int](dst.memory.data) + bufferOffset), src, size) @@ -134,13 +142,13 @@ dst.memory.flush() else: # use staging buffer, slower but required if memory is not host visible var stagingBuffer = dst.device.createBuffer(size, [VK_BUFFER_USAGE_TRANSFER_SRC_BIT], requireMappable = true, preferVRAM = false, preferAutoFlush = true) - setData(stagingBuffer, src, size, 0) - stagingBuffer.copy(dst, bufferOffset) + setData(stagingBuffer, queue, src, size, 0) + stagingBuffer.copy(dst, queue, bufferOffset) stagingBuffer.destroy() -proc setData*[T: seq](dst: Buffer, src: ptr T, offset = 0'u64) = - dst.setData(src, sizeof(get(genericParams(T), 0)) * src[].len, offset = offset) +proc setData*[T: seq](dst: Buffer, queue: Queue, src: ptr T, offset = 0'u64) = + dst.setData(queue, src, sizeof(get(genericParams(T), 0)) * src[].len, offset = offset) -proc setData*[T](dst: Buffer, src: ptr T, offset = 0'u64) = - dst.setData(src, sizeof(T), offset = offset) +proc setData*[T](dst: Buffer, queue: Queue, src: ptr T, offset = 0'u64) = + dst.setData(queue, src, sizeof(T), offset = offset) diff -r 8526d1da2f69 -r 925197a1ec95 semicongine/vulkan/commandbuffer.nim --- a/semicongine/vulkan/commandbuffer.nim Fri Mar 29 16:28:30 2024 +0700 +++ b/semicongine/vulkan/commandbuffer.nim Sat Mar 30 20:29:33 2024 +0700 @@ -1,3 +1,5 @@ +import std/strformat + import ../core import ./device import ./physicaldevice @@ -18,7 +20,7 @@ ) result.family = family result.device = device - checkVkResult device.vk.vkCreateCommandPool(addr(createInfo), nil, addr(result.vk)) + checkVkResult device.vk.vkCreateCommandPool(addr createInfo, nil, addr result.vk) var allocInfo = VkCommandBufferAllocateInfo( sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, @@ -27,20 +29,33 @@ commandBufferCount: uint32(nBuffers), ) result.buffers = newSeq[VkCommandBuffer](nBuffers) - checkVkResult device.vk.vkAllocateCommandBuffers(addr(allocInfo), result.buffers.toCPointer) + checkVkResult device.vk.vkAllocateCommandBuffers(addr allocInfo, result.buffers.toCPointer) + +proc pipelineBarrier*( + commandBuffer: VkCommandBuffer, + memoryBarriers: openArray[VkMemoryBarrier2] = [], + bufferMemoryBarriers: openArray[VkBufferMemoryBarrier2] = [], + imageBarriers: openArray[VkImageMemoryBarrier2] = [], +) = + let dependencies = VkDependencyInfo( + sType: VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + dependencyFlags: VkDependencyFlags(0), + memoryBarrierCount: uint32(memoryBarriers.len), + pMemoryBarriers: memoryBarriers.toCPointer, + bufferMemoryBarrierCount: uint32(bufferMemoryBarriers.len), + pBufferMemoryBarriers: bufferMemoryBarriers.toCPointer, + imageMemoryBarrierCount: uint32(imageBarriers.len), + pImageMemoryBarriers: imageBarriers.toCPointer, + ) + + vkCmdPipelineBarrier2(commandBuffer, addr dependencies) -template withSingleUseCommandBuffer*(device: Device, needsTransfer: bool, commandBuffer, body: untyped): untyped = +template withSingleUseCommandBuffer*(device: Device, queue: Queue, needsTransfer: bool, commandBuffer, body: untyped): untyped = assert device.vk.valid + assert queue.vk.valid - var queue: Queue - for q in device.queues.values: - if q.family.canDoTransfer or not needsTransfer: - queue = q - break - if not queue.vk.valid: - raise newException(Exception, "No queue that supports buffer transfer") - + checkVkResult queue.vk.vkQueueWaitIdle() var commandBufferPool = createCommandBufferPool(device, queue.family, 1) commandBuffer = commandBufferPool.buffers[0] @@ -48,7 +63,7 @@ sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, flags: VkCommandBufferUsageFlags(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT), ) - checkVkResult commandBuffer.vkBeginCommandBuffer(addr(beginInfo)) + checkVkResult commandBuffer.vkBeginCommandBuffer(addr beginInfo) block: body @@ -57,9 +72,9 @@ var submitInfo = VkSubmitInfo( sType: VK_STRUCTURE_TYPE_SUBMIT_INFO, commandBufferCount: 1, - pCommandBuffers: addr(commandBuffer), + pCommandBuffers: addr commandBuffer, ) - checkVkResult queue.vk.vkQueueSubmit(1, addr(submitInfo), VkFence(0)) + checkVkResult queue.vk.vkQueueSubmit(1, addr submitInfo, VkFence(0)) checkVkResult queue.vk.vkQueueWaitIdle() commandBufferPool.destroy() @@ -68,3 +83,4 @@ assert commandpool.vk.valid commandpool.device.vk.vkDestroyCommandPool(commandpool.vk, nil) commandpool.vk.reset + diff -r 8526d1da2f69 -r 925197a1ec95 semicongine/vulkan/device.nim --- a/semicongine/vulkan/device.nim Fri Mar 29 16:28:30 2024 +0700 +++ b/semicongine/vulkan/device.nim Sat Mar 30 20:29:33 2024 +0700 @@ -50,9 +50,14 @@ ) var queueList = deviceQueues.values.toSeq + var features13 = VkPhysicalDeviceVulkan13Features( + stype: VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, + synchronization2: true + ) var features2 = VkPhysicalDeviceFeatures2( stype: VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, features: result.enabledFeatures, + pNext: addr features13, ) var createInfo = VkDeviceCreateInfo( sType: VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, diff -r 8526d1da2f69 -r 925197a1ec95 semicongine/vulkan/image.nim --- a/semicongine/vulkan/image.nim Fri Mar 29 16:28:30 2024 +0700 +++ b/semicongine/vulkan/image.nim Sat Mar 30 20:29:33 2024 +0700 @@ -79,48 +79,39 @@ ) checkVkResult image.device.vk.vkBindImageMemory(image.vk, image.memory.vk, VkDeviceSize(0)) -proc transitionImageLayout(image: VulkanImage, oldLayout, newLayout: VkImageLayout) = - var barrier = VkImageMemoryBarrier( - sType: VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - oldLayout: oldLayout, - newLayout: newLayout, - srcQueueFamilyIndex: VK_QUEUE_FAMILY_IGNORED, - dstQueueFamilyIndex: VK_QUEUE_FAMILY_IGNORED, - image: image.vk, - subresourceRange: VkImageSubresourceRange( - aspectMask: toBits [VK_IMAGE_ASPECT_COLOR_BIT], - baseMipLevel: 0, - levelCount: 1, - baseArrayLayer: 0, - layerCount: 1, +proc transitionImageLayout(image: VulkanImage, queue: Queue, oldLayout, newLayout: VkImageLayout) = + var barrier = VkImageMemoryBarrier2( + sType: VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + oldLayout: oldLayout, + newLayout: newLayout, + srcQueueFamilyIndex: VK_QUEUE_FAMILY_IGNORED, + dstQueueFamilyIndex: VK_QUEUE_FAMILY_IGNORED, + image: image.vk, + subresourceRange: VkImageSubresourceRange( + aspectMask: toBits [VK_IMAGE_ASPECT_COLOR_BIT], + baseMipLevel: 0, + levelCount: 1, + baseArrayLayer: 0, + layerCount: 1, ), ) - var - sourceStage, destinationStage: VkPipelineStageFlagBits if oldLayout == VK_IMAGE_LAYOUT_UNDEFINED and newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - barrier.srcAccessMask = VkAccessFlags(0) - barrier.dstAccessMask = toBits [VK_ACCESS_TRANSFER_WRITE_BIT] - sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT - destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT + barrier.srcStageMask = [VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT].toBits + barrier.srcAccessMask = VkAccessFlags2(0) + barrier.dstStageMask = [VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT].toBits + barrier.dstAccessMask = [VK_ACCESS_2_TRANSFER_WRITE_BIT].toBits elif oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL and newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - barrier.srcAccessMask = toBits [VK_ACCESS_TRANSFER_WRITE_BIT] - barrier.dstAccessMask = toBits [VK_ACCESS_SHADER_READ_BIT] - sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT - destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT + barrier.srcStageMask = [VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT].toBits + barrier.srcAccessMask = [VK_ACCESS_2_TRANSFER_WRITE_BIT].toBits + barrier.dstStageMask = [VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT].toBits + barrier.dstAccessMask = [VK_ACCESS_2_SHADER_READ_BIT].toBits else: raise newException(Exception, "Unsupported layout transition!") - withSingleUseCommandBuffer(image.device, false, commandBuffer): - vkCmdPipelineBarrier( - commandBuffer, - toBits [sourceStage], toBits [destinationStage], - VkDependencyFlags(0), - 0, nil, - 0, nil, - 1, addr barrier - ) + withSingleUseCommandBuffer(image.device, queue, false, commandBuffer): + commandBuffer.pipelineBarrier(imageBarriers = [barrier]) -proc copy*(src: Buffer, dst: VulkanImage) = +proc copy*(src: Buffer, dst: VulkanImage, queue: Queue) = assert src.device.vk.valid assert dst.device.vk.valid assert src.device == dst.device @@ -140,7 +131,7 @@ imageOffset: VkOffset3D(x: 0, y: 0, z: 0), imageExtent: VkExtent3D(width: uint32(dst.width), height: uint32(dst.height), depth: 1) ) - withSingleUseCommandBuffer(src.device, true, commandBuffer): + withSingleUseCommandBuffer(src.device, queue, true, commandBuffer): commandBuffer.vkCmdCopyBufferToImage( src.vk, dst.vk, @@ -150,7 +141,7 @@ ) # currently only usable for texture access from shader -proc createImage(device: Device, width, height: int, depth: PixelDepth, data: pointer): VulkanImage = +proc createImage(device: Device, queue: Queue, width, height: int, depth: PixelDepth, data: pointer): VulkanImage = assert device.vk.valid assert width > 0 assert height > 0 @@ -180,12 +171,12 @@ ) checkVkResult device.vk.vkCreateImage(addr imageInfo, nil, addr result.vk) result.allocateMemory(requireMappable = false, preferVRAM = true, preferAutoFlush = false) - result.transitionImageLayout(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) + result.transitionImageLayout(queue, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) var stagingBuffer = device.createBuffer(size = size, usage = [VK_BUFFER_USAGE_TRANSFER_SRC_BIT], requireMappable = true, preferVRAM = false, preferAutoFlush = true) - stagingBuffer.setData(src = data, size = size) - stagingBuffer.copy(result) - result.transitionImageLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + stagingBuffer.setData(queue, src = data, size = size) + stagingBuffer.copy(result, queue) + result.transitionImageLayout(queue, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) stagingBuffer.destroy() proc destroy*(image: var VulkanImage) = @@ -279,12 +270,12 @@ &"VulkanTexture({texture.image.width}x{texture.image.height})" -proc uploadTexture*(device: Device, texture: Texture): VulkanTexture = +proc uploadTexture*(device: Device, queue: Queue, texture: Texture): VulkanTexture = assert device.vk.valid if texture.isGrayscale: - result.image = createImage(device = device, width = texture.grayImage.width, height = texture.grayImage.height, depth = 1, data = addr texture.grayImage.imagedata[0]) + result.image = createImage(device = device, queue = queue, width = texture.grayImage.width, height = texture.grayImage.height, depth = 1, data = addr texture.grayImage.imagedata[0]) else: - result.image = createImage(device = device, width = texture.colorImage.width, height = texture.colorImage.height, depth = 4, data = addr texture.colorImage.imagedata[0][0]) + result.image = createImage(device = device, queue = queue, width = texture.colorImage.width, height = texture.colorImage.height, depth = 4, data = addr texture.colorImage.imagedata[0][0]) result.imageView = result.image.createImageView() result.sampler = result.image.device.createSampler(texture.sampler) diff -r 8526d1da2f69 -r 925197a1ec95 semicongine/vulkan/renderpass.nim --- a/semicongine/vulkan/renderpass.nim Fri Mar 29 16:28:30 2024 +0700 +++ b/semicongine/vulkan/renderpass.nim Sat Mar 30 20:29:33 2024 +0700 @@ -10,66 +10,21 @@ type Subpass* = object - clearColor*: Vec4f - pipelineBindPoint*: VkPipelineBindPoint - flags: VkSubpassDescriptionFlags outputs: seq[VkAttachmentReference] - depthStencil: Option[VkAttachmentReference] - shaderPipelines*: seq[(MaterialType, ShaderPipeline)] RenderPass* = object vk*: VkRenderPass device*: Device - subpasses*: seq[Subpass] + shaderPipelines*: seq[(MaterialType, ShaderPipeline)] + clearColor*: Vec4f proc createRenderPass*( device: Device, - attachments: seq[VkAttachmentDescription], - subpasses: seq[Subpass], - dependencies: seq[VkSubpassDependency], + shaders: openArray[(MaterialType, ShaderConfiguration)], + clearColor = Vec4f([0.8'f32, 0.8'f32, 0.8'f32, 1'f32]), + backFaceCulling = true, + inFlightFrames = 2, ): RenderPass = assert device.vk.valid - var pAttachments = attachments - var pSubpasses = subpasses - var pDependencies = dependencies - - var subpassesList: seq[VkSubpassDescription] - for subpass in pSubpasses.mitems: - subpassesList.add VkSubpassDescription( - flags: subpass.flags, - pipelineBindPoint: subpass.pipelineBindPoint, - inputAttachmentCount: 0, - pInputAttachments: nil, - colorAttachmentCount: uint32(subpass.outputs.len), - pColorAttachments: subpass.outputs.toCPointer, - pResolveAttachments: nil, - pDepthStencilAttachment: if subpass.depthStencil.isSome: addr(subpass.depthStencil.get) else: nil, - preserveAttachmentCount: 0, - pPreserveAttachments: nil, - ) - - var createInfo = VkRenderPassCreateInfo( - sType: VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, - attachmentCount: uint32(pAttachments.len), - pAttachments: pAttachments.toCPointer, - subpassCount: uint32(subpassesList.len), - pSubpasses: subpassesList.toCPointer, - dependencyCount: uint32(pDependencies.len), - pDependencies: pDependencies.toCPointer, - ) - result.device = device - result.subpasses = pSubpasses - checkVkResult device.vk.vkCreateRenderPass(addr(createInfo), nil, addr(result.vk)) - -proc simpleForwardRenderPass*( - device: Device, - shaders: openArray[(MaterialType, ShaderConfiguration)], - inFlightFrames = 2, - clearColor = Vec4f([0.8'f32, 0.8'f32, 0.8'f32, 1'f32]), - backFaceCulling = true, -): RenderPass = - assert device.vk.valid - for (_, shaderconfig) in shaders: - assert shaderconfig.outputs.len == 1 var attachments = @[VkAttachmentDescription( format: device.physicalDevice.getSurfaceFormats().filterSurfaceFormat().format, @@ -81,13 +36,6 @@ initialLayout: VK_IMAGE_LAYOUT_UNDEFINED, finalLayout: VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, )] - subpasses = @[ - Subpass( - pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS, - outputs: @[VkAttachmentReference(attachment: 0, layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)], - clearColor: clearColor - ) - ] # dependencies seems to be optional, TODO: benchmark difference dependencies = @[VkSubpassDependency( srcSubpass: VK_SUBPASS_EXTERNAL, @@ -97,10 +45,45 @@ dstStageMask: toBits [VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT], dstAccessMask: toBits [VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT], )] - result = device.createRenderPass(attachments = attachments, subpasses = subpasses, dependencies = dependencies) + outputs = @[ + VkAttachmentReference( + attachment: 0, + layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + ) + ] + + var subpassesList = [ + VkSubpassDescription( + flags: VkSubpassDescriptionFlags(0), + pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS, + inputAttachmentCount: 0, + pInputAttachments: nil, + colorAttachmentCount: uint32(outputs.len), + pColorAttachments: outputs.toCPointer, + pResolveAttachments: nil, + pDepthStencilAttachment: nil, + preserveAttachmentCount: 0, + pPreserveAttachments: nil, + ) + ] + + var createInfo = VkRenderPassCreateInfo( + sType: VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + attachmentCount: uint32(attachments.len), + pAttachments: attachments.toCPointer, + subpassCount: uint32(subpassesList.len), + pSubpasses: subpassesList.toCPointer, + dependencyCount: uint32(dependencies.len), + pDependencies: dependencies.toCPointer, + ) + result.device = device + result.clearColor = clearColor + checkVkResult device.vk.vkCreateRenderPass(addr(createInfo), nil, addr(result.vk)) + + for (_, shaderconfig) in shaders: + assert shaderconfig.outputs.len == 1 for (materialtype, shaderconfig) in shaders: - result.subpasses[0].shaderPipelines.add (materialtype, device.createPipeline(result.vk, shaderconfig, inFlightFrames, 0, backFaceCulling = backFaceCulling)) - + result.shaderPipelines.add (materialtype, device.createPipeline(result.vk, shaderconfig, inFlightFrames, 0, backFaceCulling = backFaceCulling)) proc beginRenderCommands*(commandBuffer: VkCommandBuffer, renderpass: RenderPass, framebuffer: Framebuffer, oneTimeSubmit: bool) = assert commandBuffer.valid @@ -110,9 +93,7 @@ w = framebuffer.dimension.x h = framebuffer.dimension.y - var clearColors: seq[VkClearValue] - for subpass in renderpass.subpasses: - clearColors.add(VkClearValue(color: VkClearColorValue(float32: subpass.clearColor))) + var clearColors = [VkClearValue(color: VkClearColorValue(float32: renderpass.clearColor))] var beginInfo = VkCommandBufferBeginInfo( sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, @@ -158,7 +139,5 @@ assert renderPass.vk.valid renderPass.device.vk.vkDestroyRenderPass(renderPass.vk, nil) renderPass.vk.reset - for i in 0 ..< renderPass.subpasses.len: - for _, pipeline in renderPass.subpasses[i].shaderPipelines.mitems: - pipeline.destroy() - renderPass.subpasses = @[] + for _, pipeline in renderPass.shaderPipelines.mitems: + pipeline.destroy() diff -r 8526d1da2f69 -r 925197a1ec95 semicongine/vulkan/swapchain.nim --- a/semicongine/vulkan/swapchain.nim Fri Mar 29 16:28:30 2024 +0700 +++ b/semicongine/vulkan/swapchain.nim Sat Mar 30 20:29:33 2024 +0700 @@ -7,7 +7,6 @@ import ./physicaldevice import ./image import ./framebuffer -import ./commandbuffer import ./syncing type @@ -20,15 +19,12 @@ framebuffers*: seq[Framebuffer] currentInFlight*: int currentFramebufferIndex: uint32 - framesRendered*: uint64 queueFinishedFence*: seq[Fence] imageAvailableSemaphore*: seq[Semaphore] renderFinishedSemaphore*: seq[Semaphore] - commandBufferPool: CommandBufferPool # required for recreation: renderPass: VkRenderPass surfaceFormat: VkSurfaceFormatKHR - queueFamily: QueueFamily imageCount: uint32 inFlightFrames*: int @@ -37,7 +33,6 @@ device: Device, renderPass: VkRenderPass, surfaceFormat: VkSurfaceFormatKHR, - queueFamily: QueueFamily, desiredNumberOfImages = 3'u32, inFlightFrames = 2, oldSwapchain = VkSwapchainKHR(0), @@ -82,7 +77,6 @@ surfaceFormat: surfaceFormat, dimension: TVec2[uint32]([capabilities.currentExtent.width, capabilities.currentExtent.height]), inFlightFrames: inFlightFrames, - queueFamily: queueFamily, renderPass: renderPass ) @@ -101,7 +95,6 @@ swapchain.queueFinishedFence.add device.createFence() swapchain.imageAvailableSemaphore.add device.createSemaphore() swapchain.renderFinishedSemaphore.add device.createSemaphore() - swapchain.commandBufferPool = device.createCommandBufferPool(queueFamily, swapchain.inFlightFrames) debug &"Created swapchain with: {nImages} framebuffers, {inFlightFrames} in-flight frames, {swapchain.dimension.x}x{swapchain.dimension.y}" result = some(swapchain) else: @@ -112,7 +105,7 @@ assert swapchain.vk.valid swapchain.framebuffers[swapchain.currentFramebufferIndex] -proc nextFrame*(swapchain: var Swapchain): Option[VkCommandBuffer] = +proc nextFrame*(swapchain: var Swapchain): Option[int] = assert swapchain.device.vk.valid assert swapchain.vk.valid @@ -129,18 +122,17 @@ if nextImageResult == VK_SUCCESS: swapchain.queueFinishedFence[swapchain.currentInFlight].reset() - result = some(swapchain.commandBufferPool.buffers[swapchain.currentInFlight]) + result = some(swapchain.currentInFlight) else: - result = none(VkCommandBuffer) + result = none(int) -proc swap*(swapchain: var Swapchain): bool = +proc swap*(swapchain: var Swapchain, commandBuffer: VkCommandBuffer): bool = assert swapchain.device.vk.valid assert swapchain.vk.valid assert swapchain.device.firstGraphicsQueue().isSome assert swapchain.device.firstPresentationQueue().isSome var - commandBuffer = swapchain.commandBufferPool.buffers[swapchain.currentInFlight] waitSemaphores = [swapchain.imageAvailableSemaphore[swapchain.currentInFlight].vk] waitStages = [VkPipelineStageFlags(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT)] submitInfo = VkSubmitInfo( @@ -173,13 +165,11 @@ if presentResult != VK_SUCCESS: return false - inc swapchain.framesRendered return true proc destroy*(swapchain: var Swapchain) = assert swapchain.vk.valid - assert swapchain.commandBufferPool.vk.valid for imageview in swapchain.imageviews.mitems: assert imageview.vk.valid @@ -187,7 +177,6 @@ for framebuffer in swapchain.framebuffers.mitems: assert framebuffer.vk.valid framebuffer.destroy() - swapchain.commandBufferPool.destroy() for i in 0 ..< swapchain.inFlightFrames: assert swapchain.queueFinishedFence[i].vk.valid assert swapchain.imageAvailableSemaphore[i].vk.valid @@ -206,7 +195,6 @@ device = swapchain.device, renderPass = swapchain.renderPass, surfaceFormat = swapchain.surfaceFormat, - queueFamily = swapchain.queueFamily, desiredNumberOfImages = swapchain.imageCount, inFlightFrames = swapchain.inFlightFrames, oldSwapchain = swapchain.vk,