# HG changeset patch # User sam # Date 1718851064 -25200 # Node ID 46fae89cffb06853e15e80adb4cfddbacf21f468 # Parent dbca0528c71492839bd0542b4b1a6282c3296846 advance evenmore! diff -r dbca0528c714 -r 46fae89cffb0 semicongine/vulkan/renderpass.nim --- a/semicongine/vulkan/renderpass.nim Wed Jun 19 13:50:18 2024 +0700 +++ b/semicongine/vulkan/renderpass.nim Thu Jun 20 09:37:44 2024 +0700 @@ -1,34 +1,14 @@ import ../core -import ../material -import ./device -import ./physicaldevice -import ./pipeline -import ./shader import ./framebuffer -type - RenderPass* = object - vk*: VkRenderPass - device*: Device - shaderPipelines*: seq[(MaterialType, ShaderPipeline)] - clearColor*: Vec4f - proc CreateRenderPass*( - device: Device, - 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 - - # some asserts - for (materialtype, shaderconfig) in shaders: - shaderconfig.AssertCanRender(materialtype) + device: VkDevice, + format: VkFormat, +): VkRenderPass = var attachments = @[VkAttachmentDescription( - format: device.physicalDevice.GetSurfaceFormats().FilterSurfaceFormat().format, + format: format, samples: VK_SAMPLE_COUNT_1_BIT, loadOp: VK_ATTACHMENT_LOAD_OP_CLEAR, storeOp: VK_ATTACHMENT_STORE_OP_STORE, @@ -77,24 +57,16 @@ dependencyCount: uint32(dependencies.len), pDependencies: dependencies.ToCPointer, ) - result.device = device - result.clearColor = clearColor - checkVkResult device.vk.vkCreateRenderPass(addr(createInfo), nil, addr(result.vk)) + checkVkResult device.vkCreateRenderPass(addr(createInfo), nil, addr(result)) - for (_, shaderconfig) in shaders: - assert shaderconfig.outputs.len == 1 - for (materialtype, shaderconfig) in shaders: - result.shaderPipelines.add (materialtype, device.CreatePipeline(result.vk, shaderconfig, inFlightFrames, 0, backFaceCulling = backFaceCulling)) - -proc BeginRenderCommands*(commandBuffer: VkCommandBuffer, renderpass: RenderPass, framebuffer: Framebuffer, oneTimeSubmit: bool) = +proc BeginRenderCommands*(commandBuffer: VkCommandBuffer, renderpass: VkRenderPass, framebuffer: Framebuffer, oneTimeSubmit: bool, clearColor: Vec4f) = assert commandBuffer.Valid - assert renderpass.vk.Valid assert framebuffer.vk.Valid let w = framebuffer.dimension.x h = framebuffer.dimension.y - var clearColors = [VkClearValue(color: VkClearColorValue(float32: renderpass.clearColor))] + var clearColors = [VkClearValue(color: VkClearColorValue(float32: clearColor))] var beginInfo = VkCommandBufferBeginInfo( sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, @@ -103,7 +75,7 @@ ) renderPassInfo = VkRenderPassBeginInfo( sType: VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, - renderPass: renderPass.vk, + renderPass: renderpass, framebuffer: framebuffer.vk, renderArea: VkRect2D( offset: VkOffset2D(x: 0, y: 0), @@ -133,12 +105,3 @@ proc EndRenderCommands*(commandBuffer: VkCommandBuffer) = commandBuffer.vkCmdEndRenderPass() checkVkResult commandBuffer.vkEndCommandBuffer() - - -proc Destroy*(renderPass: var RenderPass) = - assert renderPass.device.vk.Valid - assert renderPass.vk.Valid - renderPass.device.vk.vkDestroyRenderPass(renderPass.vk, nil) - renderPass.vk.Reset - for _, pipeline in renderPass.shaderPipelines.mitems: - pipeline.Destroy() diff -r dbca0528c714 -r 46fae89cffb0 static_utils.nim --- a/static_utils.nim Wed Jun 19 13:50:18 2024 +0700 +++ b/static_utils.nim Thu Jun 20 09:37:44 2024 +0700 @@ -1,6 +1,7 @@ +import std/os import std/macros import std/strformat -import std/typetraits +import std/typetraits as tt import semicongine/core/utils import semicongine/core/imagetypes @@ -11,8 +12,10 @@ template VertexAttribute* {.pragma.} template InstanceAttribute* {.pragma.} -template DescriptorAttribute* {.pragma.} - +template Descriptor* {.pragma.} +template Pass* {.pragma.} +template PassFlat* {.pragma.} +template ShaderOutput* {.pragma.} type SupportedGPUType* = float32 | float64 | int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | TVec2[int32] | TVec2[int64] | TVec3[int32] | TVec3[int64] | TVec4[int32] | TVec4[int64] | TVec2[uint32] | TVec2[uint64] | TVec3[uint32] | TVec3[uint64] | TVec4[uint32] | TVec4[uint64] | TVec2[float32] | TVec2[float64] | TVec3[float32] | TVec3[float64] | TVec4[float32] | TVec4[float64] | TMat2[float32] | TMat2[float64] | TMat23[float32] | TMat23[float64] | TMat32[float32] | TMat32[float64] | TMat3[float32] | TMat3[float64] | TMat34[float32] | TMat34[float64] | TMat43[float32] | TMat43[float64] | TMat4[float32] | TMat4[float64] @@ -46,26 +49,68 @@ 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 Mat2[float32]: VK_FORMAT_R32G32_SFLOAT - elif T is Mat2[float64]: VK_FORMAT_R64G64_SFLOAT - elif T is Mat23[float32]: VK_FORMAT_R32G32B32_SFLOAT - elif T is Mat23[float64]: VK_FORMAT_R64G64B64_SFLOAT - elif T is Mat32[float32]: VK_FORMAT_R32G32_SFLOAT - elif T is Mat32[float64]: VK_FORMAT_R64G64_SFLOAT - elif T is Mat3[float32]: VK_FORMAT_R32G32B32_SFLOAT - elif T is Mat3[float64]: VK_FORMAT_R64G64B64_SFLOAT - elif T is Mat34[float32]: VK_FORMAT_R32G32B32A32_SFLOAT - elif T is Mat34[float64]: VK_FORMAT_R64G64B64A64_SFLOAT - elif T is Mat43[float32]: VK_FORMAT_R32G32B32_SFLOAT - elif T is Mat43[float64]: VK_FORMAT_R64G64B64_SFLOAT - elif T is Mat4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT - elif T is Mat4[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 GlslType[T: SupportedGPUType](value: T): string = + when T is float32: "float" + elif T is float64: "double" + elif T is int8, int16, int32, int64: "int" + elif T is uint8, uint16, uint32, 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 TMat23F32]: "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".} template getElementType(field: typed): untyped = when not (typeof(field) is seq or typeof(field) is array): - {.error: "getElementType can only be used with seq or array".} - genericParams(typeof(field)).get(0) + typeof(field) + # {.error: "getElementType can only be used with seq or array".} + else: + genericParams(typeof(field)).get(0) template ForVertexDataFields*(inputData: typed, fieldname, valuename, isinstancename, body: untyped): untyped = for theFieldname, value in fieldPairs(inputData): @@ -76,31 +121,33 @@ {.error: "field '" & theFieldname & "' is not a supported GPU type".} block: let `fieldname` {.inject.} = theFieldname - let `valuename` {.inject.} = default(getElementType(value)) - let `isinstancename` {.inject.} = value.isInstanceAttribute() + let `valuename` {.inject.} = value + let `isinstancename` {.inject.} = hasCustomPragma(value, InstanceAttribute) body template ForDescriptorFields*(inputData: typed, fieldname, valuename, typename, countname, body: untyped): untyped = for theFieldname, value in fieldPairs(inputData): - when hasCustomPragma(value, DescriptorAttribute): + when hasCustomPragma(value, Descriptor): when not ( - typeof(value) is SupportedGPUType - or (typeof(value) is array and elementType(value) is SupportedGPUType) - or typeof(value) is Texture + typeof(value) is SupportedGPUType or + typeof(value) is Texture or + (typeof(value) is array and getElementType(value) is SupportedGPUType) ): - {.error: "field '" & theFieldname & "' needs to be a SupportedGPUType or an array of SupportedGPUType".} + {.error: "field '" & theFieldname & "' needs to be a SupportedGPUType or an array of SupportedGPUType or a Texture".} block: let `fieldname` {.inject.} = theFieldname let `valuename` {.inject.} = default(getElementType(value)) - # TODO - let `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER - let `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER + when typeof(value) is Texture or (typeof(value) is array and getElementType(value) is Texture): + let `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER + else: + let `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER - when typeof(value) is array: - let `countname` {.inject.} = genericParams(typeof(value)).get(0) + when typeof(value) is SupportedGPUType or typeof(value) is Texture: + let `countname` {.inject.} = 1'u32 else: - let `countname` {.inject.} = 1 + assert typeof(value) is array + let `countname` {.inject.} = uint32(genericParams(typeof(value)).get(0)) body func NumberOfVertexInputAttributeDescriptors[T: SupportedGPUType](value: T): uint32 = @@ -113,18 +160,30 @@ else: 1 -func NLocationSlots[T: SupportedGPUType](value: T): uint32 = +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 typeof(value) is TVec3 and sizeof(getElementType(value)) == 8: - return 2 - elif typeof(value) is TVec4 and sizeof(getElementType(value)) == 8: + 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 @@ -146,13 +205,11 @@ indexBuffer: VkBuffer indexCount: uint32 indexBufferOffset: VkDeviceSize - Pipeline[TShaderInputs] = object + Pipeline[TShader] = object pipeline: VkPipeline layout: VkPipelineLayout descriptorSets: array[2, seq[VkDescriptorSet]] - ShaderSet[TShaderInputs] = object - vertexShader: VkShaderModule - fragmentShader: VkShaderModule + converter toVkIndexType(indexType: IndexType): VkIndexType = case indexType: of None: VK_INDEX_TYPE_NONE_KHR @@ -160,35 +217,137 @@ of UInt16: VK_INDEX_TYPE_UINT16 of UInt32: VK_INDEX_TYPE_UINT32 +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: "" -proc CreatePipeline*[TShaderInputs]( + 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, ", entrypoint ", entrypoint + 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 {entrypoint} -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 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 + var passLocation = 0 + var fsOutputLocation = 0 + var binding = 0 + + for fieldname, value in fieldPairs(shader): + # vertex shader inputs + if 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 + if 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 + if hasCustomPragma(value, ShaderOutput): + fsOutput.add &"layout(location = {fsOutputLocation}) out {GlslType(value)} {fieldname};" + fsOutputLocation.inc + if hasCustomPragma(value, Descriptor): + # TODO; samplers and uniforms + if typeof(value) is Texture: + else: + + 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 CompileShader[TShader](shader: TShader): (seq[uint32], seq[uint32]) {.compileTime.} = + let (vertexShaderSource, fragmentShaderSource) = generateShaderSource(shader) + ( + compileGlslToSPIRV(VK_SHADER_STAGE_VERTEX_BIT, vertexShaderSource), + compileGlslToSPIRV(VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShaderSource) + ) + + +proc CreatePipeline*[TShader]( device: VkDevice, renderPass: VkRenderPass, - shaderSet: ShaderSet[TShaderInputs], + vertexShader: VkShaderModule, + fragmentShader: VkShaderModule, 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, -): Pipeline[TShaderInputs] = +): Pipeline[TShader] = # assumptions/limitations: # - we are only using vertex and fragment shaders (2 stages) # - we only support one subpass - - # CONTINUE HERE, WITH PIPELINE LAYOUT!!!! - # Rely on TShaderInputs + # = we only support one Uniform-Block var layoutbindings: seq[VkDescriptorSetLayoutBinding] - let descriptors = [ - (VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1), # more than 1 for arrays - (VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1), - ] var descriptorBindingNumber = 0'u32 - ForDescriptorFields(default(TShaderInputs), fieldname, value, descriptorCount): + ForDescriptorFields(default(TShader), fieldname, value, descriptorType, descriptorCount): + # TODO: Only one binding needed for a Uniforms block layoutbindings.add VkDescriptorSetLayoutBinding( binding: descriptorBindingNumber, descriptorType: descriptorType, descriptorCount: descriptorCount, - stageFlags: VK_SHADER_STAGE_ALL_GRAPHICS, + stageFlags: VkShaderStageFlags(VK_SHADER_STAGE_ALL_GRAPHICS), pImmutableSamplers: nil, ) inc descriptorBindingNumber @@ -198,7 +357,7 @@ pBindings: layoutbindings.ToCPointer ) var descriptorSetLayout: VkDescriptorSetLayout - checkVkResult vkCreateDescriptorSetLayout(device.vk, addr(layoutCreateInfo), nil, addr(descriptorSetLayout)) + checkVkResult vkCreateDescriptorSetLayout(device, addr(layoutCreateInfo), nil, addr(descriptorSetLayout)) let pipelineLayoutInfo = VkPipelineLayoutCreateInfo( sType: VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, setLayoutCount: 1, @@ -212,22 +371,22 @@ VkPipelineShaderStageCreateInfo( sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, stage: VK_SHADER_STAGE_VERTEX_BIT, - module: shaderSet.vertexShader, + module: vertexShader, pName: "main", ), VkPipelineShaderStageCreateInfo( sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, stage: VK_SHADER_STAGE_FRAGMENT_BIT, - module: shaderSet.fragmentShader, + module: fragmentShader, pName: "main", ), ] - let - bindings: var seq[VkVertexInputBindingDescription] - attributes: var seq[VkVertexInputAttributeDescription] + var + bindings: seq[VkVertexInputBindingDescription] + attributes: seq[VkVertexInputAttributeDescription] var inputBindingNumber = 0'u32 - var inputLocationNumber = 0'u32 - ForVertexDataFields(default(TShaderInputs), fieldname, value, isInstanceAttr): + var location = 0'u32 + ForVertexDataFields(default(TShader), fieldname, value, isInstanceAttr): bindings.add VkVertexInputBindingDescription( binding: inputBindingNumber, stride: sizeof(value).uint32, @@ -238,11 +397,11 @@ for i in 0'u32 ..< NumberOfVertexInputAttributeDescriptors(value): attributes.add VkVertexInputAttributeDescription( binding: inputBindingNumber, - inputLocationNumber: inputLocationNumber, + location: location, format: VkType(value), offset: i * perDescriptorSize, ) - inputLocationNumber += NLocationSlots(value) + location += NLocationSlots(value) inc inputBindingNumber let @@ -310,7 +469,7 @@ let createInfo = VkGraphicsPipelineCreateInfo( sType: VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, stageCount: 2, - pStages: addr(stages), + pStages: stages.ToCPointer, pVertexInputState: addr(vertexInputInfo), pInputAssemblyState: addr(inputAssembly), pViewportState: addr(viewportState), @@ -353,52 +512,52 @@ nil, ) -proc AssertCompatible(TShaderInputs, TMesh, TInstance, TGlobals: typedesc) = - # assert seq-fields of TMesh|TInstance == seq-fields of TShaderInputs +proc AssertCompatible(TShader, TMesh, TInstance, TGlobals: typedesc) = + # assert seq-fields of TMesh|TInstance == seq-fields of TShader # assert normal fields of TMesh|Globals == normal fields of TShaderDescriptors - for inputName, inputValue in default(TShaderInputs).fieldPairs: + for inputName, inputValue in default(TShader).fieldPairs: echo "checking shader input '" & inputName & "'" var foundField = false when hasCustomPragma(inputValue, VertexAttribute): echo " is vertex attribute" for meshName, meshValue in default(TMesh).fieldPairs: when meshName == inputName: - assert foundField == false, "Shader input '" & TShaderInputs.name & "." & inputName & "' has been found more than once" - assert getElementType(meshValue) is typeof(inputValue), "Shader input " & TShaderInputs.name & "." & inputName & " is of type '" & typeof(inputValue).name & "' but mesh attribute is of type '" & getElementType(meshValue).name & "'" + assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" + assert getElementType(meshValue) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but mesh attribute is of type '" & tt.name(getElementType(meshValue)) & "'" foundField = true - assert foundField, "Shader input '" & TShaderInputs.name & "." & inputName & ": " & typeof(inputValue).name & "' not found in '" & TMesh.name & "'" + assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TMesh) & "'" elif hasCustomPragma(inputValue, InstanceAttribute): echo " is instance attribute" for instanceName, instanceValue in default(TInstance).fieldPairs: when instanceName == inputName: - assert foundField == false, "Shader input '" & TShaderInputs.name & "." & inputName & "' has been found more than once" - assert getElementType(instanceValue) is typeof(inputValue), "Shader input " & TShaderInputs.name & "." & inputName & " is of type '" & typeof(inputValue).name & "' but instance attribute is of type '" & getElementType(instanceValue).name & "'" + assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" + assert getElementType(instanceValue) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but instance attribute is of type '" & tt.name(getElementType(instanceValue)) & "'" foundField = true - assert foundField, "Shader input '" & TShaderInputs.name & "." & inputName & ": " & typeof(inputValue).name & "' not found in '" & TInstance.name & "'" - elif hasCustomPragma(inputValue, DescriptorAttribute): + assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TInstance) & "'" + elif hasCustomPragma(inputValue, Descriptor): echo " is descriptor attribute" for meshName, meshValue in default(TMesh).fieldPairs: when meshName == inputName: - assert foundField == false, "Shader input '" & TShaderInputs.name & "." & inputName & "' has been found more than once" - assert typeof(meshValue) is typeof(inputValue), "Shader input " & TShaderInputs.name & "." & inputName & " is of type '" & typeof(inputValue).name & "' but mesh attribute is of type '" & getElementType(meshValue).name & "'" + assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" + assert typeof(meshValue) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but mesh attribute is of type '" & tt.name(getElementType(meshValue)) & "'" foundField = true for globalName, globalValue in default(TGlobals).fieldPairs: when globalName == inputName: - assert foundField == false, "Shader input '" & TShaderInputs.name & "." & inputName & "' has been found more than once" - assert typeof(globalValue) is typeof(inputValue), "Shader input " & TShaderInputs.name & "." & inputName & " is of type '" & typeof(inputValue).name & "' but global attribute is of type '" & typeof(globalValue).name & "'" + assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" + assert typeof(globalValue) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but global attribute is of type '" & tt.name(typeof(globalValue)) & "'" foundField = true - assert foundField, "Shader input '" & TShaderInputs.name & "." & inputName & ": " & typeof(inputValue).name & "' not found in '" & TMesh.name & "|" & TGlobals.name & "'" + assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TMesh) & "|" & tt.name(TGlobals) & "'" echo " found" -proc Render[TShaderInputs, TMesh, TInstance, TGlobals]( - pipeline: Pipeline[TShaderInputs], +proc Render[TShader, TMesh, TInstance, TGlobals]( + pipeline: Pipeline[TShader], renderable: Renderable[TMesh, TInstance], globals: TGlobals, commandBuffer: VkCommandBuffer, ) = static: - AssertCompatible(TShaderInputs, TMesh, TInstance, TGlobals) + AssertCompatible(TShader, TMesh, TInstance, TGlobals) commandBuffer.vkCmdBindVertexBuffers( firstBinding = 0'u32, bindingCount = uint32(renderable.vertexBuffers.len), @@ -427,6 +586,13 @@ ) when isMainModule: + import semicongine/platform/window + import semicongine/core/vulkanapi + import semicongine/vulkan/instance + import semicongine/vulkan/device + import semicongine/vulkan/physicaldevice + import semicongine/vulkan/renderpass + type MeshA = object position: seq[Vec3f] @@ -434,18 +600,42 @@ InstanceA = object transform: seq[Mat4] position: seq[Vec3f] + other: seq[array[3, int32]] Globals = object - color: Vec4f + fontAtlas: Texture - ShaderInputsA = object + ShaderA = object position {.VertexAttribute.}: Vec3f transform {.InstanceAttribute.}: Mat4 - color {.DescriptorAttribute.}: Vec4f + fontAtlas {.Descriptor.}: Texture + other {.InstanceAttribute.}: array[3, int32] + test {.Pass.}: float32 + test1 {.PassFlat.}: Vec3f + color {.ShaderOutput.}: Vec4f + vertexCode: string = "void main() {}" + fragmentCode: string = "void main() {}" - var p: Pipeline[ShaderInputsA] + let w = CreateWindow("test2") + putEnv("VK_LAYER_ENABLES", "VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_AMD,VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_NVIDIA,VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXTVK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT,VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT") + let i = w.CreateInstance( + vulkanVersion = VK_MAKE_API_VERSION(0, 1, 3, 0), + instanceExtensions = @[], + layers = @["VK_LAYER_KHRONOS_validation"], + ) + + const (a, b) = CompileShader(Shader(A)) + + let selectedPhysicalDevice = i.GetPhysicalDevices().FilterBestGraphics() + let d = i.CreateDevice( + selectedPhysicalDevice, + enabledExtensions = @[], + selectedPhysicalDevice.FilterForGraphicsPresentationQueues() + ) + + var p: Pipeline[ShaderA] var r: Renderable[MeshA, InstanceA] var g: Globals - var s: ShaderSet[ShaderInputsA] - var p1 = CreatePipeline(device = VkDevice(0), renderPass = VkRenderPass(0), shaderSet = s) + let rp = CreateRenderPass(d.vk, d.physicalDevice.GetSurfaceFormats().FilterSurfaceFormat().format) + var p1 = CreatePipeline[ShaderA](device = d.vk, renderPass = rp, VkShaderModule(0), VkShaderModule(0)) Render(p, r, g, VkCommandBuffer(0))