Mercurial > games > semicongine
changeset 560:8de8a2102071
add: vertex and (initial) shader types and methods
author | Sam <sam@basx.dev> |
---|---|
date | Tue, 14 Mar 2023 13:21:40 +0700 |
parents | baaa620887b4 |
children | 4a9fc283c3af |
files | src/semicongine/engine.nim src/semicongine/shader.nim src/semicongine/vertex.nim src/semicongine/vulkan.nim src/semicongine/vulkan/pipeline.nim src/semicongine/vulkan/renderpass.nim src/semicongine/vulkan/shader.nim src/semicongine/vulkan/syncing.nim src/semicongine/vulkan/vertex.nim tests/test_vulkan_wrapper.nim |
diffstat | 10 files changed, 431 insertions(+), 48 deletions(-) [+] |
line wrap: on
line diff
--- a/src/semicongine/engine.nim Mon Mar 06 23:50:21 2023 +0700 +++ b/src/semicongine/engine.nim Tue Mar 14 13:21:40 2023 +0700 @@ -294,15 +294,11 @@ ) checkVkResult device.vkCreateRenderPass(addr(renderPassCreateInfo), nil, addr(result)) -proc initRenderPipeline[VertexType, Uniforms](device: VkDevice, - frameSize: TVec2[uint32], renderPass: VkRenderPass, vertexShader, - fragmentShader: static string): RenderPipeline[VertexType, Uniforms] = +proc initRenderPipeline[VertexType, Uniforms](device: VkDevice, frameSize: TVec2[uint32], renderPass: VkRenderPass, vertexShader, fragmentShader: static string): RenderPipeline[VertexType, Uniforms] = # load shaders result.device = device - result.shaders.add(initShaderProgram[VertexType, Uniforms](device, - VK_SHADER_STAGE_VERTEX_BIT, vertexShader)) - result.shaders.add(initShaderProgram[VertexType, Uniforms](device, - VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShader)) + result.shaders.add(initShaderProgram[VertexType, Uniforms](device, VK_SHADER_STAGE_VERTEX_BIT, vertexShader)) + result.shaders.add(initShaderProgram[VertexType, Uniforms](device, VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShader)) var # define which parts can be dynamic (pipeline is fixed after setup) @@ -395,8 +391,7 @@ pushConstantRangeCount: 0, pPushConstantRanges: nil, ) - checkVkResult vkCreatePipelineLayout(device, addr(pipelineLayoutInfo), nil, - addr(result.layout)) + checkVkResult vkCreatePipelineLayout(device, addr(pipelineLayoutInfo), nil, addr(result.layout)) var stages: seq[VkPipelineShaderStageCreateInfo] for shader in result.shaders: @@ -559,9 +554,7 @@ ) = result.vulkan.device.device.setupSyncPrimitives() -proc setupPipeline*[VertexType; UniformType; IndexType: uint16|uint32]( - engine: var Engine, scenedata: Thing, vertexShader, - fragmentShader: static string): RenderPipeline[VertexType, UniformType] = +proc setupPipeline*[VertexType; UniformType; IndexType: uint16|uint32](engine: var Engine, scenedata: Thing, vertexShader, fragmentShader: static string): RenderPipeline[VertexType, UniformType] = engine.currentscenedata = scenedata result = initRenderPipeline[VertexType, UniformType]( engine.vulkan.device.device, @@ -670,8 +663,7 @@ vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.layout, 0, 1, addr(pipeline.descriptors[currentFrame]), 0, nil) - for (vertexBufferSet, indexed, indexBuffer, count, indexType) in - pipeline.vertexBuffers: + for (vertexBufferSet, indexed, indexBuffer, count, indexType) in pipeline.vertexBuffers: var vertexBuffers: seq[VkBuffer] offsets: seq[VkDeviceSize] @@ -679,9 +671,7 @@ vertexBuffers.add buffer.vkBuffer offsets.add VkDeviceSize(0) - vkCmdBindVertexBuffers(commandBuffer, firstBinding = 0'u32, - bindingCount = uint32(vertexBuffers.len), 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])) if indexed: vkCmdBindIndexBuffer(commandBuffer, indexBuffer.vkBuffer, VkDeviceSize(0), indexType) vkCmdDrawIndexed(commandBuffer, count, 1, 0, 0, 0) @@ -731,10 +721,8 @@ vkCmdEndRenderPass(commandBuffer) checkVkResult vkEndCommandBuffer(commandBuffer) -proc drawFrame(window: NativeWindow, vulkan: var Vulkan, currentFrame: int, - resized: bool, pipeline: var RenderPipeline) = - checkVkResult vkWaitForFences(vulkan.device.device, 1, addr( - vulkan.inFlightFences[currentFrame]), VK_TRUE, high(uint64)) +proc drawFrame(window: NativeWindow, vulkan: var Vulkan, currentFrame: int, resized: bool, pipeline: var RenderPipeline) = + checkVkResult vkWaitForFences(vulkan.device.device, 1, addr(vulkan.inFlightFences[currentFrame]), VK_TRUE, high(uint64)) var bufferImageIndex: uint32 let nextImageResult = vkAcquireNextImageKHR( vulkan.device.device, @@ -751,8 +739,7 @@ elif not (nextImageResult in [VK_SUCCESS, VK_SUBOPTIMAL_KHR]): raise newException(Exception, "Vulkan error: vkAcquireNextImageKHR returned " & $nextImageResult) - checkVkResult vkResetFences(vulkan.device.device, 1, addr( - vulkan.inFlightFences[currentFrame])) + checkVkResult vkResetFences(vulkan.device.device, 1, addr(vulkan.inFlightFences[currentFrame])) checkVkResult vkResetCommandBuffer(vulkan.device.commandBuffers[currentFrame], VkCommandBufferResetFlags(0)) @@ -772,8 +759,7 @@ signalSemaphoreCount: 1, pSignalSemaphores: addr(signalSemaphores[0]), ) - checkVkResult vkQueueSubmit(vulkan.device.graphicsQueue, 1, addr(submitInfo), - vulkan.inFlightFences[currentFrame]) + checkVkResult vkQueueSubmit(vulkan.device.graphicsQueue, 1, addr(submitInfo), vulkan.inFlightFences[currentFrame]) var presentInfo = VkPresentInfoKHR( sType: VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
--- a/src/semicongine/shader.nim Mon Mar 06 23:50:21 2023 +0700 +++ b/src/semicongine/shader.nim Tue Mar 14 13:21:40 2023 +0700 @@ -41,9 +41,7 @@ of VK_SHADER_STAGE_COMPUTE_BIT: "comp" else: "" -proc compileGLSLToSPIRV(stage: static VkShaderStageFlagBits, - shaderSource: static string, entrypoint: string): seq[ - uint32] {.compileTime.} = +proc compileGLSLToSPIRV(stage: static VkShaderStageFlagBits, shaderSource: static string, entrypoint: string): seq[uint32] {.compileTime.} = when defined(nimcheck): # will not run if nimcheck is running return result const @@ -79,9 +77,7 @@ ) i += 4 -proc initShaderProgram*[VertexType, Uniforms](device: VkDevice, - programType: static VkShaderStageFlagBits, shader: static string, - entryPoint: static string = "main"): ShaderProgram[VertexType, Uniforms] = +proc initShaderProgram*[VertexType, Uniforms](device: VkDevice, programType: static VkShaderStageFlagBits, shader: static string, entryPoint: static string = "main"): ShaderProgram[VertexType, Uniforms] = result.entryPoint = entryPoint result.programType = programType
--- a/src/semicongine/vertex.nim Mon Mar 06 23:50:21 2023 +0700 +++ b/src/semicongine/vertex.nim Tue Mar 14 13:21:40 2023 +0700 @@ -142,8 +142,7 @@ return stmtList.join("\n") -func generateInputVertexBinding*[T](bindingoffset: int = 0, - locationoffset: int = 0): seq[VkVertexInputBindingDescription] = +func generateInputVertexBinding*[T](bindingoffset: int = 0, locationoffset: int = 0): seq[VkVertexInputBindingDescription] = # packed attribute data, not interleaved (aks "struct of arrays") var binding = bindingoffset for name, value in T().fieldPairs: @@ -163,8 +162,7 @@ binding += 1 -func generateInputAttributeBinding*[T](bindingoffset: int = 0, - locationoffset: int = 0): seq[VkVertexInputAttributeDescription] = +func generateInputAttributeBinding*[T](bindingoffset: int = 0, locationoffset: int = 0): seq[VkVertexInputAttributeDescription] = # TODO: var location = 0 var binding = bindingoffset @@ -175,12 +173,9 @@ VkVertexInputAttributeDescription( binding: uint32(binding), location: uint32(location), - format: getVkFormat[compositeAttributeType(getAttributeType( - value))](), - offset: uint32(i * sizeof(compositeAttributeType(getAttributeType( - value)))), + format: getVkFormat[compositeAttributeType(getAttributeType(value))](), + offset: uint32(i * sizeof(compositeAttributeType(getAttributeType(value)))), ) ) - location += nLocationSlots[compositeAttributeType(getAttributeType( - value))]() + location += nLocationSlots[compositeAttributeType(getAttributeType(value))]() binding += 1
--- a/src/semicongine/vulkan.nim Mon Mar 06 23:50:21 2023 +0700 +++ b/src/semicongine/vulkan.nim Tue Mar 14 13:21:40 2023 +0700 @@ -25,6 +25,15 @@ import ./vulkan/syncing export syncing +import ./vulkan/shader +export shader + +import ./vulkan/vertex +export vertex + +import ./vulkan/pipeline +export pipeline + import ./vulkan/buffer export buffer
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/semicongine/vulkan/pipeline.nim Tue Mar 14 13:21:40 2023 +0700 @@ -0,0 +1,152 @@ +import ./api +import ./utils +import ./renderpass +import ./vertex +import ./device +import ./shader + +type + Pipeline = object + device: Device + vk*: VkPipeline + + +proc createPipeline*(renderPass: RenderPass, vertexShader: VertexShader, fragmentShader: FragmentShader): Pipeline = + assert renderPass.vk.valid + assert renderPass.device.vk.valid + result.device = renderPass.device + + var descriptorType: VkDescriptorType + var bindingNumber = 0'u32 + var arrayLen = 1 + var shaderStage: seq[VkShaderStageFlagBits] + var layoutbinding = VkDescriptorSetLayoutBinding( + binding: bindingNumber, + descriptorType: descriptorType, + descriptorCount: uint32(arrayLen), + stageFlags: toBits shaderStage, + pImmutableSamplers: nil, + ) + var descriptorLayoutBinding: seq[VkDescriptorSetLayoutBinding] = @[layoutbinding] + var layoutCreateInfo = VkDescriptorSetLayoutCreateInfo( + sType: VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + bindingCount: uint32(descriptorLayoutBinding.len), + pBindings: descriptorLayoutBinding.toCPointer + ) + var descriptorLayout: VkDescriptorSetLayout + checkVkResult vkCreateDescriptorSetLayout( + renderPass.device.vk, + addr(layoutCreateInfo), + nil, + addr(descriptorLayout), + ) + var pushConstant = VkPushConstantRange( + stageFlags: toBits shaderStage, + offset: 0, + size: 0, + ) + + var descriptorSets: seq[VkDescriptorSetLayout] = @[descriptorLayout] + var pushConstants: seq[VkPushConstantRange] = @[pushConstant] + var pipelineLayoutInfo = VkPipelineLayoutCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + setLayoutCount: uint32(descriptorSets.len), + pSetLayouts: descriptorSets.toCPointer, + pushConstantRangeCount: uint32(pushConstants.len), + pPushConstantRanges: pushConstants.toCPointer, + ) + var pipelineLayout: VkPipelineLayout + checkVkResult vkCreatePipelineLayout(renderPass.device.vk, addr(pipelineLayoutInfo), nil, addr(pipelineLayout)) + + var + vertexInputInfo = vertexShader.getVertexBindings() + inputAssembly = VkPipelineInputAssemblyStateCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + topology: VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + primitiveRestartEnable: VK_FALSE, + ) + viewportState = VkPipelineViewportStateCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + viewportCount: 1, + scissorCount: 1, + ) + rasterizer = VkPipelineRasterizationStateCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + depthClampEnable: VK_FALSE, + rasterizerDiscardEnable: VK_FALSE, + polygonMode: VK_POLYGON_MODE_FILL, + lineWidth: 1.0, + cullMode: toBits [VK_CULL_MODE_BACK_BIT], + frontFace: VK_FRONT_FACE_CLOCKWISE, + depthBiasEnable: VK_FALSE, + depthBiasConstantFactor: 0.0, + depthBiasClamp: 0.0, + depthBiasSlopeFactor: 0.0, + ) + multisampling = VkPipelineMultisampleStateCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + sampleShadingEnable: VK_FALSE, + rasterizationSamples: VK_SAMPLE_COUNT_1_BIT, + minSampleShading: 1.0, + pSampleMask: nil, + alphaToCoverageEnable: VK_FALSE, + alphaToOneEnable: VK_FALSE, + ) + colorBlendAttachment = VkPipelineColorBlendAttachmentState( + colorWriteMask: toBits [VK_COLOR_COMPONENT_R_BIT, VK_COLOR_COMPONENT_G_BIT, VK_COLOR_COMPONENT_B_BIT, VK_COLOR_COMPONENT_A_BIT], + blendEnable: VK_TRUE, + srcColorBlendFactor: VK_BLEND_FACTOR_SRC_ALPHA, + dstColorBlendFactor: VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, + colorBlendOp: VK_BLEND_OP_ADD, + srcAlphaBlendFactor: VK_BLEND_FACTOR_ONE, + dstAlphaBlendFactor: VK_BLEND_FACTOR_ZERO, + alphaBlendOp: VK_BLEND_OP_ADD, + ) + colorBlending = VkPipelineColorBlendStateCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + logicOpEnable: VK_TRUE, + logicOp: VK_LOGIC_OP_COPY, + attachmentCount: 1, + pAttachments: addr(colorBlendAttachment), + blendConstants: [0.0'f, 0.0'f, 0.0'f, 0.0'f], + ) + dynamicStates = @[VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR] + dynamicState = VkPipelineDynamicStateCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + dynamicStateCount: uint32(dynamicStates.len), + pDynamicStates: dynamicStates.toCPointer, + ) + var stages = @[vertexShader.getPipelineInfo(), fragmentShader.getPipelineInfo()] + var createInfo = VkGraphicsPipelineCreateInfo( + sType: VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + stageCount: uint32(stages.len), + pStages: stages.toCPointer, + pVertexInputState: addr(vertexInputInfo), + pInputAssemblyState: addr(inputAssembly), + pViewportState: addr(viewportState), + pRasterizationState: addr(rasterizer), + pMultisampleState: addr(multisampling), + pDepthStencilState: nil, + pColorBlendState: addr(colorBlending), + pDynamicState: addr(dynamicState), + layout: pipelineLayout, + renderPass: renderPass.vk, + subpass: 0, + basePipelineHandle: VkPipeline(0), + basePipelineIndex: -1, + ) + checkVkResult vkCreateGraphicsPipelines( + renderpass.device.vk, + VkPipelineCache(0), + 1, + addr(createInfo), + nil, + addr(result.vk) + ) + +proc destroy*(pipeline: var Pipeline) = + assert pipeline.device.vk.valid + assert pipeline.vk.valid + + pipeline.device.vk.vkDestroyPipeline(pipeline.vk, nil) + pipeline.vk.reset()
--- a/src/semicongine/vulkan/renderpass.nim Mon Mar 06 23:50:21 2023 +0700 +++ b/src/semicongine/vulkan/renderpass.nim Tue Mar 14 13:21:40 2023 +0700 @@ -15,7 +15,7 @@ preserves: seq[uint32] RenderPass* = object vk*: VkRenderPass - device: Device + device*: Device proc createRenderPass*( device: Device,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/semicongine/vulkan/shader.nim Tue Mar 14 13:21:40 2023 +0700 @@ -0,0 +1,114 @@ +import std/os +import std/hashes +import std/strformat +import std/compilesettings + +import ./api +import ./device + +type + VertexShader*[VertexType] = object + device: Device + vertexType*: VertexType + module*: VkShaderModule + FragmentShader* = object + device: Device + module*: VkShaderModule + +proc staticExecChecked(command: string, input = ""): string {.compileTime.} = + let (output, exitcode) = gorgeEx( + command = command, + input = input) + if exitcode != 0: + raise newException(Exception, &"Running '{command}' produced exit code: {exitcode}" & output) + return output + +func stage2string(stage: VkShaderStageFlagBits): string {.compileTime.} = + case stage + of VK_SHADER_STAGE_VERTEX_BIT: "vert" + of VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: "tesc" + of VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: "tese" + of VK_SHADER_STAGE_GEOMETRY_BIT: "geom" + of VK_SHADER_STAGE_FRAGMENT_BIT: "frag" + of VK_SHADER_STAGE_COMPUTE_BIT: "comp" + else: "" + +proc compileGLSLToSPIRV(stage: static VkShaderStageFlagBits, shaderSource: static string, entrypoint: string): seq[uint32] {.compileTime.} = + when defined(nimcheck): # will not run if nimcheck is running + return result + const + stagename = stage2string(stage) + shaderHash = hash(shaderSource) + # cross compilation for windows workaround, sorry computer + shaderfile = getTempDir() / &"shader_{shaderHash}.{stagename}" + projectPath = querySetting(projectPath) + + discard staticExecChecked( + command = &"{projectPath}/glslangValidator --entry-point {entrypoint} -V --stdin -S {stagename} -o {shaderfile}", + input = shaderSource + ) + + when defined(mingw) and defined(linux): # required for crosscompilation, path separators get messed up + let shaderbinary = staticRead shaderfile.replace("\\", "/") + else: + let shaderbinary = staticRead shaderfile + when defined(linux): + discard staticExecChecked(command = fmt"rm {shaderfile}") + elif defined(windows): + discard staticExecChecked(command = fmt"cmd.exe /c del {shaderfile}") + else: + raise newException(Exception, "Unsupported operating system") + + var i = 0 + while i < shaderbinary.len: + result.add( + (uint32(shaderbinary[i + 0]) shl 0) or + (uint32(shaderbinary[i + 1]) shl 8) or + (uint32(shaderbinary[i + 2]) shl 16) or + (uint32(shaderbinary[i + 3]) shl 24) + ) + i += 4 + +proc createVertexShader*[VertexType](device: Device, shader: static string, vertexType: VertexType, entryPoint: static string = "main"): VertexShader[VertexType] = + assert device.vk.valid + + const constcode = compileGLSLToSPIRV(VK_SHADER_STAGE_VERTEX_BIT, shader, entryPoint) + var code = constcode + var createInfo = VkShaderModuleCreateInfo( + sType: VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + codeSize: uint(code.len * sizeof(uint32)), + pCode: if code.len > 0: addr(code[0]) else: nil, + ) + checkVkResult vkCreateShaderModule(device.vk, addr(createInfo), nil, addr(result.module)) + +proc createFragmentShader*(device: Device, shader: static string, entryPoint: static string = "main"): FragmentShader = + assert device.vk.valid + + const constcode = compileGLSLToSPIRV(VK_SHADER_STAGE_FRAGMENT_BIT, shader, entryPoint) + var code = constcode + var createInfo = VkShaderModuleCreateInfo( + sType: VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + codeSize: uint(code.len * sizeof(uint32)), + pCode: if code.len > 0: addr(code[0]) else: nil, + ) + checkVkResult vkCreateShaderModule(device.vk, addr(createInfo), nil, addr(result.module)) + +proc getPipelineInfo*(shader: VertexShader|FragmentShader, entryPoint = "main"): VkPipelineShaderStageCreateInfo = + VkPipelineShaderStageCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + stage: VK_SHADER_STAGE_VERTEX_BIT, + module: shader.module, + pName: cstring(entryPoint), + ) + +proc destroy*(shader: var VertexShader) = + assert shader.device.vk.valid + assert shader.module.valid + shader.device.vk.vkDestroyShaderModule(shader.module, nil) + shader.module.reset + +proc destroy*(shader: var FragmentShader) = + assert shader.device.vk.valid + assert shader.module.valid + shader.device.vk.vkDestroyShaderModule(shader.module, nil) + shader.module.reset
--- a/src/semicongine/vulkan/syncing.nim Mon Mar 06 23:50:21 2023 +0700 +++ b/src/semicongine/vulkan/syncing.nim Tue Mar 14 13:21:40 2023 +0700 @@ -9,13 +9,13 @@ vk*: VkFence device: Device -proc createSemaphore(device: Device): Semaphore = +proc createSemaphore*(device: Device): Semaphore = assert device.vk.valid var semaphoreInfo = VkSemaphoreCreateInfo(sType: VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO) result.device = device checkVkResult device.vk.vkCreateSemaphore(addr(semaphoreInfo), nil, addr(result.vk)) -proc createFence(device: Device): Fence = +proc createFence*(device: Device): Fence = assert device.vk.valid var fenceInfo = VkFenceCreateInfo( sType: VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, @@ -24,13 +24,13 @@ result.device = device checkVkResult device.vk.vkCreateFence(addr(fenceInfo), nil, addr(result.vk)) -proc destroy(semaphore: var Semaphore) = +proc destroy*(semaphore: var Semaphore) = assert semaphore.device.vk.valid assert semaphore.vk.valid semaphore.device.vk.vkDestroySemaphore(semaphore.vk, nil) semaphore.vk.reset -proc destroy(fence: var Fence) = +proc destroy*(fence: var Fence) = assert fence.device.vk.valid assert fence.vk.valid fence.device.vk.vkDestroyFence(fence.vk, nil)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/semicongine/vulkan/vertex.nim Tue Mar 14 13:21:40 2023 +0700 @@ -0,0 +1,113 @@ +import std/tables +import std/macros + +import ../math +import ./api +import ./utils +import ./shader + + +# add pragma to fields of the VertexType that represent per instance attributes +template PerInstance*() {.pragma.} + +# from https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap15.html +func nLocationSlots[T](value: T): uint32 = + when (T is TVec3[float64] or T is TVec3[uint64] or T is TVec4[float64] or T is TVec4[float64]): + return 2 + elif T is SomeNumber or T is TVec: + return 1 + else: + raise newException(Exception, "Unsupported vertex attribute type") + +# return the type into which larger types are divided +func compositeAttribute[T](value: T): auto = + when T is TMat33[float32]: + Vec3() + elif T is TMat44[float32]: + Vec4() + else: + value + +# return the number of elements into which larger types are divided +func compositeAttributesNumber[T](value: T): int = + when T is TMat33[float32]: + 3 + elif T is TMat44[float32]: + 4 + else: + 1 + +func getVkFormat[T](value: T): VkFormat = + when T is uint8: VK_FORMAT_R8_UINT + elif T is int8: VK_FORMAT_R8_SINT + elif T is uint16: VK_FORMAT_R16_UINT + elif T is int16: VK_FORMAT_R16_SINT + elif T is uint32: VK_FORMAT_R32_UINT + elif T is int32: VK_FORMAT_R32_SINT + elif T is uint64: VK_FORMAT_R64_UINT + elif T is int64: VK_FORMAT_R64_SINT + elif T is float32: VK_FORMAT_R32_SFLOAT + elif T is float64: VK_FORMAT_R64_SFLOAT + elif T is TVec2[uint8]: VK_FORMAT_R8G8_UINT + elif T is TVec2[int8]: VK_FORMAT_R8G8_SINT + elif T is TVec2[uint16]: VK_FORMAT_R16G16_UINT + elif T is TVec2[int16]: VK_FORMAT_R16G16_SINT + elif T is TVec2[uint32]: VK_FORMAT_R32G32_UINT + elif T is TVec2[int32]: VK_FORMAT_R32G32_SINT + elif T is TVec2[uint64]: VK_FORMAT_R64G64_UINT + elif T is TVec2[int64]: VK_FORMAT_R64G64_SINT + elif T is TVec2[float32]: VK_FORMAT_R32G32_SFLOAT + elif T is TVec2[float64]: VK_FORMAT_R64G64_SFLOAT + elif T is TVec3[uint8]: VK_FORMAT_R8G8B8_UINT + elif T is TVec3[int8]: VK_FORMAT_R8G8B8_SINT + elif T is TVec3[uint16]: VK_FORMAT_R16G16B16_UINT + elif T is TVec3[int16]: VK_FORMAT_R16G16B16_SINT + elif T is TVec3[uint32]: VK_FORMAT_R32G32B32_UINT + elif T is TVec3[int32]: VK_FORMAT_R32G32B32_SINT + elif T is TVec3[uint64]: VK_FORMAT_R64G64B64_UINT + elif T is TVec3[int64]: VK_FORMAT_R64G64B64_SINT + elif T is TVec3[float32]: VK_FORMAT_R32G32B32_SFLOAT + elif T is TVec3[float64]: VK_FORMAT_R64G64B64_SFLOAT + elif T is TVec4[uint8]: VK_FORMAT_R8G8B8A8_UINT + elif T is TVec4[int8]: VK_FORMAT_R8G8B8A8_SINT + elif T is TVec4[uint16]: VK_FORMAT_R16G16B16A16_UINT + elif T is TVec4[int16]: VK_FORMAT_R16G16B16A16_SINT + elif T is TVec4[uint32]: VK_FORMAT_R32G32B32A32_UINT + elif T is TVec4[int32]: VK_FORMAT_R32G32B32A32_SINT + elif T is TVec4[uint64]: VK_FORMAT_R64G64B64A64_UINT + elif T is TVec4[int64]: VK_FORMAT_R64G64B64A64_SINT + elif T is TVec4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT + elif T is TVec4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT + else: {.error: "Unsupported vertex attribute type".} + +proc getVertexBindings*(shader: VertexShader): VkPipelineVertexInputStateCreateInfo = + var location = 0'u32 + var binding = 0'u32 + var offset = 0'u32 + var bindings: seq[VkVertexInputBindingDescription] + var attributes: seq[VkVertexInputAttributeDescription] + + for name, value in shader.vertexType.fieldPairs: + bindings.add VkVertexInputBindingDescription( + binding: binding, + stride: uint32(sizeof(value)), + inputRate: if value.hasCustomPragma(PerInstance): VK_VERTEX_INPUT_RATE_INSTANCE else: VK_VERTEX_INPUT_RATE_VERTEX, + ) + # allows to submit larger data structures like Mat44, for most other types will be 1 + for i in 0 ..< compositeAttributesNumber(value): + attributes.add VkVertexInputAttributeDescription( + binding: binding, + location: location, + format: getVkFormat(compositeAttribute(value)), + offset: uint32(i * sizeof(compositeAttribute(value))), + ) + location += nLocationSlots(compositeAttribute(value)) + inc binding + + return VkPipelineVertexInputStateCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + vertexBindingDescriptionCount: uint32(bindings.len), + pVertexBindingDescriptions: bindings.toCPointer, + vertexAttributeDescriptionCount: uint32(attributes.len), + pVertexAttributeDescriptions: attributes.toCPointer, + )
--- a/tests/test_vulkan_wrapper.nim Mon Mar 06 23:50:21 2023 +0700 +++ b/tests/test_vulkan_wrapper.nim Tue Mar 14 13:21:40 2023 +0700 @@ -4,6 +4,10 @@ import semicongine/platform/window import semicongine/math +type + Vertex = object + pos: Vec3 + when isMainModule: # print basic driver infos @@ -64,13 +68,27 @@ framebuffers.add device.createFramebuffer(renderpass, [imageview], swapchain.dimension) # todo: could be create inside "device", but it would be nice to have nim v2 with support for circular dependencies first - var commandPool = device.createCommandPool(family=device.firstGraphicsQueue().get().family, nBuffers=1) + var + commandPool = device.createCommandPool(family=device.firstGraphicsQueue().get().family, nBuffers=1) + imageAvailable = device.createSemaphore() + renderFinished = device.createSemaphore() + inflight = device.createFence() + + var vertexshader = device.createVertexShader("#version 450\nvoid main() {}", Vertex()) + var fragmentshader = device.createFragmentShader("#version 450\nvoid main() {}") + var pipeline = renderpass.createPipeline(vertexshader, fragmentshader) echo "All successfull" echo "Start cleanup" + # cleanup + pipeline.destroy() + vertexshader.destroy() + fragmentshader.destroy() + inflight.destroy() + imageAvailable.destroy() + renderFinished.destroy() commandPool.destroy() - # cleanup for fb in framebuffers.mitems: fb.destroy() renderpass.destroy()