Mercurial > games > semicongine
diff semiconginev2/rendering/shaders.nim @ 1218:56781cc0fc7c compiletime-tests
did: renamge main package
author | sam <sam@basx.dev> |
---|---|
date | Wed, 17 Jul 2024 21:01:37 +0700 |
parents | semicongine/rendering/shaders.nim@570ac1620fb6 |
children | 4e465583ea32 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/semiconginev2/rendering/shaders.nim Wed Jul 17 21:01:37 2024 +0700 @@ -0,0 +1,510 @@ +func GlslType[T: SupportedGPUType|Texture](value: T): string = + when T is float32: "float" + elif T is float64: "double" + elif T is int8 or T is int16 or T is int32 or T is int64: "int" + elif T is uint8 or T is uint16 or T is uint32 or T is uint64: "uint" + elif T is TVec2[int32]: "ivec2" + elif T is TVec2[int64]: "ivec2" + elif T is TVec3[int32]: "ivec3" + elif T is TVec3[int64]: "ivec3" + elif T is TVec4[int32]: "ivec4" + elif T is TVec4[int64]: "ivec4" + elif T is TVec2[uint32]: "uvec2" + elif T is TVec2[uint64]: "uvec2" + elif T is TVec3[uint32]: "uvec3" + elif T is TVec3[uint64]: "uvec3" + elif T is TVec4[uint32]: "uvec4" + elif T is TVec4[uint64]: "uvec4" + elif T is TVec2[float32]: "vec2" + elif T is TVec2[float64]: "dvec2" + elif T is TVec3[float32]: "vec3" + elif T is TVec3[float64]: "dvec3" + elif T is TVec4[float32]: "vec4" + elif T is TVec4[float64]: "dvec4" + elif T is TMat2[float32]: "mat2" + elif T is TMat2[float64]: "dmat2" + elif T is TMat23[float32]: "mat23" + elif T is TMat23[float64]: "dmat23" + elif T is TMat32[float32]: "mat32" + elif T is TMat32[float64]: "dmat32" + elif T is TMat3[float32]: "mat3" + elif T is TMat3[float64]: "dmat3" + elif T is TMat34[float32]: "mat34" + elif T is TMat34[float64]: "dmat34" + elif T is TMat43[float32]: "mat43" + elif T is TMat43[float64]: "dmat43" + elif T is TMat4[float32]: "mat4" + elif T is TMat4[float64]: "dmat4" + elif T is Texture: "sampler2D" + else: {.error: "Unsupported data type on GPU".} + +func VkType[T: SupportedGPUType](value: T): VkFormat = + when T is float32: VK_FORMAT_R32_SFLOAT + elif T is float64: VK_FORMAT_R64_SFLOAT + elif T is int8: VK_FORMAT_R8_SINT + elif T is int16: VK_FORMAT_R16_SINT + elif T is int32: VK_FORMAT_R32_SINT + elif T is int64: VK_FORMAT_R64_SINT + elif T is uint8: VK_FORMAT_R8_UINT + elif T is uint16: VK_FORMAT_R16_UINT + elif T is uint32: VK_FORMAT_R32_UINT + elif T is uint64: VK_FORMAT_R64_UINT + elif T is TVec2[int32]: VK_FORMAT_R32G32_SINT + elif T is TVec2[int64]: VK_FORMAT_R64G64_SINT + elif T is TVec3[int32]: VK_FORMAT_R32G32B32_SINT + elif T is TVec3[int64]: VK_FORMAT_R64G64B64_SINT + elif T is TVec4[int32]: VK_FORMAT_R32G32B32A32_SINT + elif T is TVec4[int64]: VK_FORMAT_R64G64B64A64_SINT + elif T is TVec2[uint32]: VK_FORMAT_R32G32_UINT + elif T is TVec2[uint64]: VK_FORMAT_R64G64_UINT + elif T is TVec3[uint32]: VK_FORMAT_R32G32B32_UINT + elif T is TVec3[uint64]: VK_FORMAT_R64G64B64_UINT + elif T is TVec4[uint32]: VK_FORMAT_R32G32B32A32_UINT + elif T is TVec4[uint64]: VK_FORMAT_R64G64B64A64_UINT + elif T is TVec2[float32]: VK_FORMAT_R32G32_SFLOAT + elif T is TVec2[float64]: VK_FORMAT_R64G64_SFLOAT + elif T is TVec3[float32]: VK_FORMAT_R32G32B32_SFLOAT + elif T is TVec3[float64]: VK_FORMAT_R64G64B64_SFLOAT + elif T is TVec4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT + elif T is TVec4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT + elif T is TMat2[float32]: VK_FORMAT_R32G32_SFLOAT + elif T is TMat2[float64]: VK_FORMAT_R64G64_SFLOAT + elif T is TMat23[float32]: VK_FORMAT_R32G32B32_SFLOAT + elif T is TMat23[float64]: VK_FORMAT_R64G64B64_SFLOAT + elif T is TMat32[float32]: VK_FORMAT_R32G32_SFLOAT + elif T is TMat32[float64]: VK_FORMAT_R64G64_SFLOAT + elif T is TMat3[float32]: VK_FORMAT_R32G32B32_SFLOAT + elif T is TMat3[float64]: VK_FORMAT_R64G64B64_SFLOAT + elif T is TMat34[float32]: VK_FORMAT_R32G32B32A32_SFLOAT + elif T is TMat34[float64]: VK_FORMAT_R64G64B64A64_SFLOAT + elif T is TMat43[float32]: VK_FORMAT_R32G32B32_SFLOAT + elif T is TMat43[float64]: VK_FORMAT_R64G64B64_SFLOAT + elif T is TMat4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT + elif T is TMat4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT + else: {.error: "Unsupported data type on GPU".} + + +func NumberOfVertexInputAttributeDescriptors[T: SupportedGPUType|Texture](value: T): uint32 = + when T is TMat2[float32] or T is TMat2[float64] or T is TMat23[float32] or T is TMat23[float64]: + 2 + elif T is TMat32[float32] or T is TMat32[float64] or T is TMat3[float32] or T is TMat3[float64] or T is TMat34[float32] or T is TMat34[float64]: + 3 + elif T is TMat43[float32] or T is TMat43[float64] or T is TMat4[float32] or T is TMat4[float64]: + 4 + else: + 1 + +func NLocationSlots[T: SupportedGPUType|Texture](value: T): uint32 = + #[ + single location: + - any scalar + - any 16-bit vector + - any 32-bit vector + - any 64-bit vector that has max. 2 components + 16-bit scalar and vector types, and + 32-bit scalar and vector types, and + 64-bit scalar and 2-component vector types. + two locations + 64-bit three- and four-component vectors + ]# + when T is TVec3[int64] or + T is TVec4[int64] or + T is TVec3[uint64] or + T is TVec4[uint64] or + T is TVec3[float64] or + T is TVec4[float64] or + T is TMat23[float64] or + T is TMat3[float64] or + T is TMat34[float64] or + T is TMat43[float64] or + T is TMat4[float64]: + return 2 + else: + return 1 + +proc generateShaderSource[TShader](shader: TShader): (string, string) {.compileTime.} = + const GLSL_VERSION = "450" + var vsInput: seq[string] + var vsOutput: seq[string] + var fsInput: seq[string] + var fsOutput: seq[string] + var uniforms: seq[string] + var samplers: seq[string] + var vsInputLocation = 0'u32 + var passLocation = 0 + var fsOutputLocation = 0 + + var sawDescriptorSets = false + for fieldname, value in fieldPairs(shader): + # vertex shader inputs + when hasCustomPragma(value, VertexAttribute) or hasCustomPragma(value, InstanceAttribute): + assert typeof(value) is SupportedGPUType + vsInput.add "layout(location = " & $vsInputLocation & ") in " & GlslType(value) & " " & fieldname & ";" + for j in 0 ..< NumberOfVertexInputAttributeDescriptors(value): + vsInputLocation += NLocationSlots(value) + + # intermediate values, passed between shaders + elif hasCustomPragma(value, Pass) or hasCustomPragma(value, PassFlat): + let flat = if hasCustomPragma(value, PassFlat): "flat " else: "" + vsOutput.add "layout(location = " & $passLocation & ") " & flat & "out " & GlslType(value) & " " & fieldname & ";" + fsInput.add "layout(location = " & $passLocation & ") " & flat & "in " & GlslType(value) & " " & fieldname & ";" + passLocation.inc + + # fragment shader output + elif hasCustomPragma(value, ShaderOutput): + fsOutput.add &"layout(location = " & $fsOutputLocation & ") out " & GlslType(value) & " " & fieldname & ";" + fsOutputLocation.inc + + # descriptor sets + # need to consider 4 cases: uniform block, texture, uniform block array, texture array + elif hasCustomPragma(value, DescriptorSets): + assert not sawDescriptorSets, "Only one field with pragma DescriptorSets allowed per shader" + assert typeof(value) is tuple, "Descriptor field '" & fieldname & "' must be of type tuple" + assert tupleLen(typeof(value)) <= MAX_DESCRIPTORSETS, typetraits.name(TShader) & ": maximum " & $MAX_DESCRIPTORSETS & " allowed" + sawDescriptorSets = true + var descriptorSetIndex = 0 + for descriptor in value.fields: + + var descriptorBinding = 0 + + for descriptorName, descriptorValue in fieldPairs(descriptor): + + when typeof(descriptorValue) is Texture: + samplers.add "layout(set=" & $descriptorSetIndex & ", binding = " & $descriptorBinding & ") uniform " & GlslType(descriptorValue) & " " & descriptorName & ";" + descriptorBinding.inc + + elif typeof(descriptorValue) is GPUValue: + + uniforms.add "layout(set=" & $descriptorSetIndex & ", binding = " & $descriptorBinding & ") uniform T" & descriptorName & " {" + when typeof(descriptorValue.data) is object: + + for blockFieldName, blockFieldValue in descriptorValue.data.fieldPairs(): + assert typeof(blockFieldValue) is SupportedGPUType, "uniform block field '" & blockFieldName & "' is not a SupportedGPUType" + uniforms.add " " & GlslType(blockFieldValue) & " " & blockFieldName & ";" + uniforms.add "} " & descriptorName & ";" + + else: + {.error: "Unsupported shader descriptor field " & descriptorName & " (must be object)".} + descriptorBinding.inc + + elif typeof(descriptorValue) is array: + + when elementType(descriptorValue) is Texture: + + let arrayDecl = "[" & $typeof(descriptorValue).len & "]" + samplers.add "layout(set=" & $descriptorSetIndex & ", binding = " & $descriptorBinding & ") uniform " & GlslType(default(elementType(descriptorValue))) & " " & descriptorName & "" & arrayDecl & ";" + descriptorBinding.inc + + elif elementType(descriptorValue) is GPUValue: + + uniforms.add "layout(set=" & $descriptorSetIndex & ", binding = " & $descriptorBinding & ") uniform T" & descriptorName & " {" + + for blockFieldName, blockFieldValue in default(elementType(descriptorValue)).data.fieldPairs(): + assert typeof(blockFieldValue) is SupportedGPUType, "uniform block field '" & blockFieldName & "' is not a SupportedGPUType" + uniforms.add " " & GlslType(blockFieldValue) & " " & blockFieldName & ";" + uniforms.add "} " & descriptorName & "[" & $descriptorValue.len & "];" + descriptorBinding.inc + + else: + {.error: "Unsupported shader descriptor field " & descriptorName.} + + descriptorSetIndex.inc + elif fieldname in ["vertexCode", "fragmentCode"]: + discard + else: + {.error: "Unsupported shader field '" & typetraits.name(TShader) & "." & fieldname & "' of type " & typetraits.name(typeof(value)).} + + result[0] = (@[&"#version {GLSL_VERSION}", "#extension GL_EXT_scalar_block_layout : require", ""] & + vsInput & + uniforms & + samplers & + vsOutput & + @[shader.vertexCode]).join("\n") + + result[1] = (@[&"#version {GLSL_VERSION}", "#extension GL_EXT_scalar_block_layout : require", ""] & + fsInput & + uniforms & + samplers & + fsOutput & + @[shader.fragmentCode]).join("\n") + +proc compileGlslToSPIRV(stage: VkShaderStageFlagBits, shaderSource: string): seq[uint32] {.compileTime.} = + 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: "" + + when defined(nimcheck): # will not run if nimcheck is running + return result + + let + stagename = stage2string(stage) + shaderHash = hash(shaderSource) + shaderfile = getTempDir() / &"shader_{shaderHash}.{stagename}" + + if not shaderfile.fileExists: + echo "shader of type ", stage + for i, line in enumerate(shaderSource.splitlines()): + echo " ", i + 1, " ", line + var glslExe = currentSourcePath.parentDir.parentDir.parentDir / "tools" / "glslangValidator" + when defined(windows): + glslExe = glslExe & "." & ExeExt + let command = &"{glslExe} --entry-point main -V --stdin -S {stagename} -o {shaderfile}" + echo "run: ", command + discard StaticExecChecked( + command = command, + input = shaderSource + ) + else: + echo &"shaderfile {shaderfile} is up-to-date" + + when defined(mingw) and defined(linux): # required for crosscompilation, path separators get messed up + let shaderbinary = staticRead shaderfile.replace("\\", "/") + else: + let shaderbinary = staticRead shaderfile + + 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 CompileShader[TShader](shader: static TShader): (VkShaderModule, VkShaderModule) = + const (vertexShaderSource, fragmentShaderSource) = generateShaderSource(shader) + + let vertexBinary = compileGlslToSPIRV(VK_SHADER_STAGE_VERTEX_BIT, vertexShaderSource) + let fragmentBinary = compileGlslToSPIRV(VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShaderSource) + + var createInfoVertex = VkShaderModuleCreateInfo( + sType: VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + codeSize: csize_t(vertexBinary.len * sizeof(uint32)), + pCode: vertexBinary.ToCPointer, + ) + checkVkResult vulkan.device.vkCreateShaderModule(addr(createInfoVertex), nil, addr(result[0])) + var createInfoFragment = VkShaderModuleCreateInfo( + sType: VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + codeSize: csize_t(fragmentBinary.len * sizeof(uint32)), + pCode: fragmentBinary.ToCPointer, + ) + checkVkResult vulkan.device.vkCreateShaderModule(addr(createInfoFragment), nil, addr(result[1])) + +template ForVertexDataFields(shader: typed, fieldname, valuename, isinstancename, body: untyped): untyped = + for theFieldname, value in fieldPairs(shader): + when hasCustomPragma(value, VertexAttribute) or hasCustomPragma(value, InstanceAttribute): + when not typeof(value) is seq: + {.error: "field '" & theFieldname & "' needs to be a seq".} + when not typeof(value) is SupportedGPUType: + {.error: "field '" & theFieldname & "' is not a supported GPU type".} + block: + const `fieldname` {.inject.} = theFieldname + let `valuename` {.inject.} = value + const `isinstancename` {.inject.} = hasCustomPragma(value, InstanceAttribute) + body + +proc GetDescriptorSetCount[TShader](): uint32 = + for _, value in fieldPairs(default(TShader)): + when hasCustomPragma(value, DescriptorSets): + return tupleLen(typeof(value)).uint32 + +proc CreateDescriptorSetLayouts[TShader](): array[MAX_DESCRIPTORSETS, VkDescriptorSetLayout] = + var setNumber: int + for _, value in fieldPairs(default(TShader)): + when hasCustomPragma(value, DescriptorSets): + for descriptorSet in value.fields: + var layoutbindings: seq[VkDescriptorSetLayoutBinding] + ForDescriptorFields(descriptorSet, fieldName, fieldValue, descriptorType, descriptorCount, descriptorBindingNumber): + layoutbindings.add VkDescriptorSetLayoutBinding( + binding: descriptorBindingNumber, + descriptorType: descriptorType, + descriptorCount: descriptorCount, + stageFlags: VkShaderStageFlags(VK_SHADER_STAGE_ALL_GRAPHICS), + pImmutableSamplers: nil, + ) + var layoutCreateInfo = VkDescriptorSetLayoutCreateInfo( + sType: VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + bindingCount: layoutbindings.len.uint32, + pBindings: layoutbindings.ToCPointer + ) + checkVkResult vkCreateDescriptorSetLayout( + vulkan.device, + addr(layoutCreateInfo), + nil, + addr(result[setNumber]) + ) + inc setNumber + +proc CreatePipeline*[TShader]( + renderPass: VkRenderPass, + topology: VkPrimitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + polygonMode: VkPolygonMode = VK_POLYGON_MODE_FILL, + cullMode: VkCullModeFlagBits = VK_CULL_MODE_BACK_BIT, + frontFace: VkFrontFace = VK_FRONT_FACE_CLOCKWISE, + descriptorPoolLimit = 1024, + samples = VK_SAMPLE_COUNT_1_BIT, +): Pipeline[TShader] = + # create pipeline + + const shader = default(TShader) + (result.vertexShaderModule, result.fragmentShaderModule) = CompileShader(shader) + + var nSets = GetDescriptorSetCount[TShader]() + result.descriptorSetLayouts = CreateDescriptorSetLayouts[TShader]() + let pipelineLayoutInfo = VkPipelineLayoutCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + setLayoutCount: nSets, + pSetLayouts: if nSets == 0: nil else: result.descriptorSetLayouts.ToCPointer, + # pushConstantRangeCount: uint32(pushConstants.len), + # pPushConstantRanges: pushConstants.ToCPointer, + ) + checkVkResult vkCreatePipelineLayout(vulkan.device, addr(pipelineLayoutInfo), nil, addr(result.layout)) + + let stages = [ + VkPipelineShaderStageCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + stage: VK_SHADER_STAGE_VERTEX_BIT, + module: result.vertexShaderModule, + pName: "main", + ), + VkPipelineShaderStageCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + stage: VK_SHADER_STAGE_FRAGMENT_BIT, + module: result.fragmentShaderModule, + pName: "main", + ), + ] + var + bindings: seq[VkVertexInputBindingDescription] + attributes: seq[VkVertexInputAttributeDescription] + var inputBindingNumber = 0'u32 + var location = 0'u32 + ForVertexDataFields(default(TShader), fieldname, value, isInstanceAttr): + bindings.add VkVertexInputBindingDescription( + binding: inputBindingNumber, + stride: sizeof(value).uint32, + inputRate: if isInstanceAttr: 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 + let perDescriptorSize = sizeof(value).uint32 div NumberOfVertexInputAttributeDescriptors(value) + for i in 0'u32 ..< NumberOfVertexInputAttributeDescriptors(value): + attributes.add VkVertexInputAttributeDescription( + binding: inputBindingNumber, + location: location, + format: VkType(value), + offset: i * perDescriptorSize, + ) + location += NLocationSlots(value) + inc inputBindingNumber + + let + vertexInputInfo = 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, + ) + inputAssembly = VkPipelineInputAssemblyStateCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + topology: topology, + primitiveRestartEnable: 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: polygonMode, + lineWidth: 1.0, + cullMode: toBits [cullMode], + frontFace: frontFace, + 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: samples, + 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: false, + attachmentCount: 1, + pAttachments: addr(colorBlendAttachment), + ) + dynamicStates = [VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR] + dynamicState = VkPipelineDynamicStateCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + dynamicStateCount: dynamicStates.len.uint32, + pDynamicStates: dynamicStates.ToCPointer, + ) + let createInfo = VkGraphicsPipelineCreateInfo( + sType: VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + stageCount: 2, + 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: result.layout, + renderPass: renderPass, + subpass: 0, + basePipelineHandle: VkPipeline(0), + basePipelineIndex: -1, + ) + checkVkResult vkCreateGraphicsPipelines( + vulkan.device, + VkPipelineCache(0), + 1, + addr(createInfo), + nil, + addr(result.vk) + ) + +template WithPipeline*(commandbuffer: VkCommandBuffer, pipeline: Pipeline, body: untyped): untyped = + block: + vkCmdBindPipeline(commandbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.vk) + body + +proc DestroyPipeline*(pipeline: Pipeline) = + + for descriptorSetLayout in pipeline.descriptorSetLayouts: + vkDestroyDescriptorSetLayout(vulkan.device, descriptorSetLayout, nil) + + vkDestroyShaderModule(vulkan.device, pipeline.vertexShaderModule, nil) + vkDestroyShaderModule(vulkan.device, pipeline.fragmentShaderModule, nil) + vkDestroyPipelineLayout(vulkan.device, pipeline.layout, nil) + vkDestroyPipeline(vulkan.device, pipeline.vk, nil)