Mercurial > games > semicongine
comparison static_utils.nim @ 1163:438d32d8b14f compiletime-tests
add: more static compilation stuff, code is getting a bit crazy, but also super nice API
| author | sam <sam@basx.dev> |
|---|---|
| date | Fri, 21 Jun 2024 00:14:43 +0700 |
| parents | 46fae89cffb0 |
| children | 7b4d4d85d9f5 |
comparison
equal
deleted
inserted
replaced
| 1162:46fae89cffb0 | 1163:438d32d8b14f |
|---|---|
| 1 import std/os | 1 import std/os |
| 2 import std/enumerate | |
| 3 import std/hashes | |
| 2 import std/macros | 4 import std/macros |
| 3 import std/strformat | 5 import std/strformat |
| 6 import std/strutils | |
| 4 import std/typetraits as tt | 7 import std/typetraits as tt |
| 5 | 8 |
| 6 import semicongine/core/utils | 9 import semicongine/core/utils |
| 7 import semicongine/core/imagetypes | 10 import semicongine/core/imagetypes |
| 8 import semicongine/core/vector | 11 import semicongine/core/vector |
| 10 import semicongine/core/vulkanapi | 13 import semicongine/core/vulkanapi |
| 11 import semicongine/vulkan/buffer | 14 import semicongine/vulkan/buffer |
| 12 | 15 |
| 13 template VertexAttribute* {.pragma.} | 16 template VertexAttribute* {.pragma.} |
| 14 template InstanceAttribute* {.pragma.} | 17 template InstanceAttribute* {.pragma.} |
| 15 template Descriptor* {.pragma.} | |
| 16 template Pass* {.pragma.} | 18 template Pass* {.pragma.} |
| 17 template PassFlat* {.pragma.} | 19 template PassFlat* {.pragma.} |
| 18 template ShaderOutput* {.pragma.} | 20 template ShaderOutput* {.pragma.} |
| 19 | 21 |
| 22 const INFLIGHTFRAMES = 2 | |
| 20 type | 23 type |
| 21 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] | 24 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] |
| 25 ShaderObject*[TShader] = object | |
| 26 vertexShader: VkShaderModule | |
| 27 fragmentShader: VkShaderModule | |
| 22 | 28 |
| 23 func VkType[T: SupportedGPUType](value: T): VkFormat = | 29 func VkType[T: SupportedGPUType](value: T): VkFormat = |
| 24 when T is float32: VK_FORMAT_R32_SFLOAT | 30 when T is float32: VK_FORMAT_R32_SFLOAT |
| 25 elif T is float64: VK_FORMAT_R64_SFLOAT | 31 elif T is float64: VK_FORMAT_R64_SFLOAT |
| 26 elif T is int8: VK_FORMAT_R8_SINT | 32 elif T is int8: VK_FORMAT_R8_SINT |
| 63 elif T is TMat43[float64]: VK_FORMAT_R64G64B64_SFLOAT | 69 elif T is TMat43[float64]: VK_FORMAT_R64G64B64_SFLOAT |
| 64 elif T is TMat4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT | 70 elif T is TMat4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT |
| 65 elif T is TMat4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT | 71 elif T is TMat4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT |
| 66 else: {.error: "Unsupported data type on GPU".} | 72 else: {.error: "Unsupported data type on GPU".} |
| 67 | 73 |
| 68 func GlslType[T: SupportedGPUType](value: T): string = | 74 func GlslType[T: SupportedGPUType|Texture](value: T): string = |
| 69 when T is float32: "float" | 75 when T is float32: "float" |
| 70 elif T is float64: "double" | 76 elif T is float64: "double" |
| 71 elif T is int8, int16, int32, int64: "int" | 77 elif T is int8 or T is int16 or T is int32 or T is int64: "int" |
| 72 elif T is uint8, uint16, uint32, uint64: "uint" | 78 elif T is uint8 or T is uint16 or T is uint32 or T is uint64: "uint" |
| 73 elif T is TVec2[int32]: "ivec2" | 79 elif T is TVec2[int32]: "ivec2" |
| 74 elif T is TVec2[int64]: "ivec2" | 80 elif T is TVec2[int64]: "ivec2" |
| 75 elif T is TVec3[int32]: "ivec3" | 81 elif T is TVec3[int32]: "ivec3" |
| 76 elif T is TVec3[int64]: "ivec3" | 82 elif T is TVec3[int64]: "ivec3" |
| 77 elif T is TVec4[int32]: "ivec4" | 83 elif T is TVec4[int32]: "ivec4" |
| 88 elif T is TVec3[float64]: "dvec3" | 94 elif T is TVec3[float64]: "dvec3" |
| 89 elif T is TVec4[float32]: "vec4" | 95 elif T is TVec4[float32]: "vec4" |
| 90 elif T is TVec4[float64]: "dvec4" | 96 elif T is TVec4[float64]: "dvec4" |
| 91 elif T is TMat2[float32]: "mat2" | 97 elif T is TMat2[float32]: "mat2" |
| 92 elif T is TMat2[float64]: "dmat2" | 98 elif T is TMat2[float64]: "dmat2" |
| 93 elif T is TMat23F32]: "mat23" | 99 elif T is TMat23[float32]: "mat23" |
| 94 elif T is TMat23[float64]: "dmat23" | 100 elif T is TMat23[float64]: "dmat23" |
| 95 elif T is TMat32[float32]: "mat32" | 101 elif T is TMat32[float32]: "mat32" |
| 96 elif T is TMat32[float64]: "dmat32" | 102 elif T is TMat32[float64]: "dmat32" |
| 97 elif T is TMat3[float32]: "mat3" | 103 elif T is TMat3[float32]: "mat3" |
| 98 elif T is TMat3[float64]: "dmat3" | 104 elif T is TMat3[float64]: "dmat3" |
| 103 elif T is TMat4[float32]: "mat4" | 109 elif T is TMat4[float32]: "mat4" |
| 104 elif T is TMat4[float64]: "dmat4" | 110 elif T is TMat4[float64]: "dmat4" |
| 105 elif T is Texture: "sampler2D" | 111 elif T is Texture: "sampler2D" |
| 106 else: {.error: "Unsupported data type on GPU".} | 112 else: {.error: "Unsupported data type on GPU".} |
| 107 | 113 |
| 108 template getElementType(field: typed): untyped = | |
| 109 when not (typeof(field) is seq or typeof(field) is array): | |
| 110 typeof(field) | |
| 111 # {.error: "getElementType can only be used with seq or array".} | |
| 112 else: | |
| 113 genericParams(typeof(field)).get(0) | |
| 114 | |
| 115 template ForVertexDataFields*(inputData: typed, fieldname, valuename, isinstancename, body: untyped): untyped = | 114 template ForVertexDataFields*(inputData: typed, fieldname, valuename, isinstancename, body: untyped): untyped = |
| 116 for theFieldname, value in fieldPairs(inputData): | 115 for theFieldname, value in fieldPairs(inputData): |
| 117 when hasCustomPragma(value, VertexAttribute) or hasCustomPragma(value, InstanceAttribute): | 116 when hasCustomPragma(value, VertexAttribute) or hasCustomPragma(value, InstanceAttribute): |
| 118 when not typeof(value) is seq: | 117 when not typeof(value) is seq: |
| 119 {.error: "field '" & theFieldname & "' needs to be a seq".} | 118 {.error: "field '" & theFieldname & "' needs to be a seq".} |
| 123 let `fieldname` {.inject.} = theFieldname | 122 let `fieldname` {.inject.} = theFieldname |
| 124 let `valuename` {.inject.} = value | 123 let `valuename` {.inject.} = value |
| 125 let `isinstancename` {.inject.} = hasCustomPragma(value, InstanceAttribute) | 124 let `isinstancename` {.inject.} = hasCustomPragma(value, InstanceAttribute) |
| 126 body | 125 body |
| 127 | 126 |
| 128 template ForDescriptorFields*(inputData: typed, fieldname, valuename, typename, countname, body: untyped): untyped = | 127 template ForDescriptorFields*(inputData: typed, typename, countname, body: untyped): untyped = |
| 129 for theFieldname, value in fieldPairs(inputData): | 128 for theFieldname, value in fieldPairs(inputData): |
| 130 when hasCustomPragma(value, Descriptor): | 129 when typeof(value) is Texture: |
| 131 when not ( | |
| 132 typeof(value) is SupportedGPUType or | |
| 133 typeof(value) is Texture or | |
| 134 (typeof(value) is array and getElementType(value) is SupportedGPUType) | |
| 135 ): | |
| 136 {.error: "field '" & theFieldname & "' needs to be a SupportedGPUType or an array of SupportedGPUType or a Texture".} | |
| 137 block: | 130 block: |
| 138 let `fieldname` {.inject.} = theFieldname | 131 let `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER |
| 139 let `valuename` {.inject.} = default(getElementType(value)) | 132 let `countname` {.inject.} = 1'u32 |
| 140 | 133 body |
| 141 when typeof(value) is Texture or (typeof(value) is array and getElementType(value) is Texture): | 134 elif typeof(value) is object: |
| 135 block: | |
| 136 let `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER | |
| 137 let `countname` {.inject.} = 1'u32 | |
| 138 body | |
| 139 elif typeof(value) is array: | |
| 140 when elementType(value) is Texture: | |
| 141 block: | |
| 142 let `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER | 142 let `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER |
| 143 else: | 143 let `countname` {.inject.} = uint32(typeof(value).len) |
| 144 body | |
| 145 elif elementType(value) is object: | |
| 146 block: | |
| 144 let `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER | 147 let `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER |
| 145 | 148 let `countname` {.inject.} = uint32(typeof(value).len) |
| 146 when typeof(value) is SupportedGPUType or typeof(value) is Texture: | 149 body |
| 147 let `countname` {.inject.} = 1'u32 | 150 |
| 148 else: | 151 func NumberOfVertexInputAttributeDescriptors[T: SupportedGPUType|Texture](value: T): uint32 = |
| 149 assert typeof(value) is array | |
| 150 let `countname` {.inject.} = uint32(genericParams(typeof(value)).get(0)) | |
| 151 body | |
| 152 | |
| 153 func NumberOfVertexInputAttributeDescriptors[T: SupportedGPUType](value: T): uint32 = | |
| 154 when T is TMat2[float32] or T is TMat2[float64] or T is TMat23[float32] or T is TMat23[float64]: | 152 when T is TMat2[float32] or T is TMat2[float64] or T is TMat23[float32] or T is TMat23[float64]: |
| 155 2 | 153 2 |
| 156 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]: | 154 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]: |
| 157 3 | 155 3 |
| 158 elif T is TMat43[float32] or T is TMat43[float64] or T is TMat4[float32] or T is TMat4[float64]: | 156 elif T is TMat43[float32] or T is TMat43[float64] or T is TMat4[float32] or T is TMat4[float64]: |
| 206 indexCount: uint32 | 204 indexCount: uint32 |
| 207 indexBufferOffset: VkDeviceSize | 205 indexBufferOffset: VkDeviceSize |
| 208 Pipeline[TShader] = object | 206 Pipeline[TShader] = object |
| 209 pipeline: VkPipeline | 207 pipeline: VkPipeline |
| 210 layout: VkPipelineLayout | 208 layout: VkPipelineLayout |
| 211 descriptorSets: array[2, seq[VkDescriptorSet]] | 209 descriptorSets: array[INFLIGHTFRAMES, seq[VkDescriptorSet]] |
| 212 | 210 |
| 213 converter toVkIndexType(indexType: IndexType): VkIndexType = | 211 converter toVkIndexType(indexType: IndexType): VkIndexType = |
| 214 case indexType: | 212 case indexType: |
| 215 of None: VK_INDEX_TYPE_NONE_KHR | 213 of None: VK_INDEX_TYPE_NONE_KHR |
| 216 of UInt8: VK_INDEX_TYPE_UINT8_EXT | 214 of UInt8: VK_INDEX_TYPE_UINT8_EXT |
| 234 let | 232 let |
| 235 stagename = stage2string(stage) | 233 stagename = stage2string(stage) |
| 236 shaderHash = hash(shaderSource) | 234 shaderHash = hash(shaderSource) |
| 237 shaderfile = getTempDir() / &"shader_{shaderHash}.{stagename}" | 235 shaderfile = getTempDir() / &"shader_{shaderHash}.{stagename}" |
| 238 | 236 |
| 239 | |
| 240 if not shaderfile.fileExists: | 237 if not shaderfile.fileExists: |
| 241 echo "shader of type ", stage, ", entrypoint ", entrypoint | 238 echo "shader of type ", stage |
| 242 for i, line in enumerate(shaderSource.splitlines()): | 239 for i, line in enumerate(shaderSource.splitlines()): |
| 243 echo " ", i + 1, " ", line | 240 echo " ", i + 1, " ", line |
| 244 var glslExe = currentSourcePath.parentDir.parentDir.parentDir / "tools" / "glslangValidator" | 241 # var glslExe = currentSourcePath.parentDir.parentDir.parentDir / "tools" / "glslangValidator" |
| 242 var glslExe = currentSourcePath.parentDir / "tools" / "glslangValidator" | |
| 245 when defined(windows): | 243 when defined(windows): |
| 246 glslExe = glslExe & "." & ExeExt | 244 glslExe = glslExe & "." & ExeExt |
| 247 let command = &"{glslExe} --entry-point {entrypoint} -V --stdin -S {stagename} -o {shaderfile}" | 245 let command = &"{glslExe} --entry-point main -V --stdin -S {stagename} -o {shaderfile}" |
| 248 echo "run: ", command | 246 echo "run: ", command |
| 249 discard StaticExecChecked( | 247 discard StaticExecChecked( |
| 250 command = command, | 248 command = command, |
| 251 input = shaderSource | 249 input = shaderSource |
| 252 ) | 250 ) |
| 274 var vsOutput: seq[string] | 272 var vsOutput: seq[string] |
| 275 var fsInput: seq[string] | 273 var fsInput: seq[string] |
| 276 var fsOutput: seq[string] | 274 var fsOutput: seq[string] |
| 277 var uniforms: seq[string] | 275 var uniforms: seq[string] |
| 278 var samplers: seq[string] | 276 var samplers: seq[string] |
| 279 var vsInputLocation = 0 | 277 var vsInputLocation = 0'u32 |
| 280 var passLocation = 0 | 278 var passLocation = 0 |
| 281 var fsOutputLocation = 0 | 279 var fsOutputLocation = 0 |
| 282 var binding = 0 | 280 var descriptorBinding = 0 |
| 283 | 281 |
| 284 for fieldname, value in fieldPairs(shader): | 282 for fieldname, value in fieldPairs(shader): |
| 285 # vertex shader inputs | 283 # vertex shader inputs |
| 286 if hasCustomPragma(value, VertexAttribute) or hasCustomPragma(value, InstanceAttribute): | 284 when hasCustomPragma(value, VertexAttribute) or hasCustomPragma(value, InstanceAttribute): |
| 287 assert typeof(value) is SupportedGPUType | 285 assert typeof(value) is SupportedGPUType |
| 288 vsInput.add &"layout(location = {vsInputLocation}) in {GlslType(value)} {fieldname};" | 286 vsInput.add "layout(location = " & $vsInputLocation & ") in " & GlslType(value) & " " & fieldname & ";" |
| 289 for j in 0 ..< NumberOfVertexInputAttributeDescriptors(value): | 287 for j in 0 ..< NumberOfVertexInputAttributeDescriptors(value): |
| 290 vsInputLocation += NLocationSlots(value) | 288 vsInputLocation += NLocationSlots(value) |
| 291 # intermediate values, passed between shaders | 289 # intermediate values, passed between shaders |
| 292 if hasCustomPragma(value, Pass) or hasCustomPragma(value, PassFlat): | 290 elif hasCustomPragma(value, Pass) or hasCustomPragma(value, PassFlat): |
| 293 let flat = if hasCustomPragma(value, PassFlat): "flat " else "" | 291 let flat = if hasCustomPragma(value, PassFlat): "flat " else: "" |
| 294 vsOutput.add &"layout(location = {passLocation}) {flat}out {GlslType(value)} {fieldname};" | 292 vsOutput.add "layout(location = " & $passLocation & ") " & flat & "out " & GlslType(value) & " " & fieldname & ";" |
| 295 fsInput.add &"layout(location = {passLocation}) {flat}in {GlslType(value)} {fieldname};" | 293 fsInput.add "layout(location = " & $passLocation & ") " & flat & "in " & GlslType(value) & " " & fieldname & ";" |
| 296 passLocation.inc | 294 passLocation.inc |
| 297 if hasCustomPragma(value, ShaderOutput): | 295 elif hasCustomPragma(value, ShaderOutput): |
| 298 fsOutput.add &"layout(location = {fsOutputLocation}) out {GlslType(value)} {fieldname};" | 296 fsOutput.add &"layout(location = " & $fsOutputLocation & ") out " & GlslType(value) & " " & fieldname & ";" |
| 299 fsOutputLocation.inc | 297 fsOutputLocation.inc |
| 300 if hasCustomPragma(value, Descriptor): | 298 elif typeof(value) is Texture: |
| 301 # TODO; samplers and uniforms | 299 samplers.add "layout(binding = " & $descriptorBinding & ") uniform " & GlslType(value) & " " & fieldname & ";" |
| 302 if typeof(value) is Texture: | 300 descriptorBinding.inc |
| 301 elif typeof(value) is object: | |
| 302 # TODO | |
| 303 uniforms.add "" | |
| 304 descriptorBinding.inc | |
| 305 elif typeof(value) is array: | |
| 306 when elementType(value) is Texture: | |
| 307 let arrayDecl = "[" & $typeof(value).len & "]" | |
| 308 samplers.add "layout(binding = " & $descriptorBinding & ") uniform " & GlslType(default(elementType(value))) & " " & fieldname & "" & arrayDecl & ";" | |
| 309 descriptorBinding.inc | |
| 310 elif elementType(value) is object: | |
| 311 # TODO | |
| 312 let arrayDecl = "[" & $typeof(value).len & "]" | |
| 313 # uniforms.add "layout(binding = " & $descriptorBinding & ") uniform " & GlslType(elementType(value)) & " " & fieldname & "" & arrayDecl & ";" | |
| 314 descriptorBinding.inc | |
| 303 else: | 315 else: |
| 316 {.error: "Unsupported shader field " & fieldname.} | |
| 317 elif fieldname in ["vertexCode", "fragmentCode"]: | |
| 318 discard | |
| 319 else: | |
| 320 {.error: "Unsupported shader field '" & tt.name(TShader) & "." & fieldname & "' of type " & tt.name(typeof(value)).} | |
| 304 | 321 |
| 305 result[0] = (@[&"#version {GLSL_VERSION}", "#extension GL_EXT_scalar_block_layout : require", ""] & | 322 result[0] = (@[&"#version {GLSL_VERSION}", "#extension GL_EXT_scalar_block_layout : require", ""] & |
| 306 vsInput & | 323 vsInput & |
| 307 uniforms & | 324 uniforms & |
| 308 samplers & | 325 samplers & |
| 314 uniforms & | 331 uniforms & |
| 315 samplers & | 332 samplers & |
| 316 fsOutput & | 333 fsOutput & |
| 317 @[shader.fragmentCode]).join("\n") | 334 @[shader.fragmentCode]).join("\n") |
| 318 | 335 |
| 319 proc CompileShader[TShader](shader: TShader): (seq[uint32], seq[uint32]) {.compileTime.} = | 336 # proc CompileShader[TShader](shader: static TShader): (seq[uint32], seq[uint32]) {.compileTime.}= |
| 320 let (vertexShaderSource, fragmentShaderSource) = generateShaderSource(shader) | 337 proc CompileShader[TShader](device: VkDevice, shader: static TShader): ShaderObject[TShader] = |
| 321 ( | 338 const (vertexShaderSource, fragmentShaderSource) = generateShaderSource(shader) |
| 322 compileGlslToSPIRV(VK_SHADER_STAGE_VERTEX_BIT, vertexShaderSource), | 339 |
| 323 compileGlslToSPIRV(VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShaderSource) | 340 let vertexBinary = compileGlslToSPIRV(VK_SHADER_STAGE_VERTEX_BIT, vertexShaderSource) |
| 324 ) | 341 let fragmentBinary = compileGlslToSPIRV(VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShaderSource) |
| 342 | |
| 343 var createInfoVertex = VkShaderModuleCreateInfo( | |
| 344 sType: VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, | |
| 345 codeSize: csize_t(vertexBinary.len * sizeof(uint32)), | |
| 346 pCode: vertexBinary.ToCPointer, | |
| 347 ) | |
| 348 checkVkResult device.vkCreateShaderModule(addr(createInfoVertex), nil, addr(result.vertexShader)) | |
| 349 var createInfoFragment = VkShaderModuleCreateInfo( | |
| 350 sType: VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, | |
| 351 codeSize: csize_t(fragmentBinary.len * sizeof(uint32)), | |
| 352 pCode: fragmentBinary.ToCPointer, | |
| 353 ) | |
| 354 checkVkResult device.vkCreateShaderModule(addr(createInfoFragment), nil, addr(result.fragmentShader)) | |
| 325 | 355 |
| 326 | 356 |
| 327 proc CreatePipeline*[TShader]( | 357 proc CreatePipeline*[TShader]( |
| 328 device: VkDevice, | 358 device: VkDevice, |
| 329 renderPass: VkRenderPass, | 359 renderPass: VkRenderPass, |
| 330 vertexShader: VkShaderModule, | 360 shader: ShaderObject[TShader], |
| 331 fragmentShader: VkShaderModule, | |
| 332 topology: VkPrimitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, | 361 topology: VkPrimitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, |
| 333 polygonMode: VkPolygonMode = VK_POLYGON_MODE_FILL, | 362 polygonMode: VkPolygonMode = VK_POLYGON_MODE_FILL, |
| 334 cullMode: VkCullModeFlagBits = VK_CULL_MODE_BACK_BIT, | 363 cullMode: VkCullModeFlagBits = VK_CULL_MODE_BACK_BIT, |
| 335 frontFace: VkFrontFace = VK_FRONT_FACE_CLOCKWISE, | 364 frontFace: VkFrontFace = VK_FRONT_FACE_CLOCKWISE, |
| 336 ): Pipeline[TShader] = | 365 ): Pipeline[TShader] = |
| 339 # - we only support one subpass | 368 # - we only support one subpass |
| 340 # = we only support one Uniform-Block | 369 # = we only support one Uniform-Block |
| 341 | 370 |
| 342 var layoutbindings: seq[VkDescriptorSetLayoutBinding] | 371 var layoutbindings: seq[VkDescriptorSetLayoutBinding] |
| 343 var descriptorBindingNumber = 0'u32 | 372 var descriptorBindingNumber = 0'u32 |
| 344 ForDescriptorFields(default(TShader), fieldname, value, descriptorType, descriptorCount): | 373 ForDescriptorFields(default(TShader), descriptorType, descriptorCount): |
| 345 # TODO: Only one binding needed for a Uniforms block | |
| 346 layoutbindings.add VkDescriptorSetLayoutBinding( | 374 layoutbindings.add VkDescriptorSetLayoutBinding( |
| 347 binding: descriptorBindingNumber, | 375 binding: descriptorBindingNumber, |
| 348 descriptorType: descriptorType, | 376 descriptorType: descriptorType, |
| 349 descriptorCount: descriptorCount, | 377 descriptorCount: descriptorCount, |
| 350 stageFlags: VkShaderStageFlags(VK_SHADER_STAGE_ALL_GRAPHICS), | 378 stageFlags: VkShaderStageFlags(VK_SHADER_STAGE_ALL_GRAPHICS), |
| 369 | 397 |
| 370 let stages = [ | 398 let stages = [ |
| 371 VkPipelineShaderStageCreateInfo( | 399 VkPipelineShaderStageCreateInfo( |
| 372 sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | 400 sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
| 373 stage: VK_SHADER_STAGE_VERTEX_BIT, | 401 stage: VK_SHADER_STAGE_VERTEX_BIT, |
| 374 module: vertexShader, | 402 module: shader.vertexShader, |
| 375 pName: "main", | 403 pName: "main", |
| 376 ), | 404 ), |
| 377 VkPipelineShaderStageCreateInfo( | 405 VkPipelineShaderStageCreateInfo( |
| 378 sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | 406 sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
| 379 stage: VK_SHADER_STAGE_FRAGMENT_BIT, | 407 stage: VK_SHADER_STAGE_FRAGMENT_BIT, |
| 380 module: fragmentShader, | 408 module: shader.fragmentShader, |
| 381 pName: "main", | 409 pName: "main", |
| 382 ), | 410 ), |
| 383 ] | 411 ] |
| 384 var | 412 var |
| 385 bindings: seq[VkVertexInputBindingDescription] | 413 bindings: seq[VkVertexInputBindingDescription] |
| 504 commandBuffer.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline) | 532 commandBuffer.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline) |
| 505 commandBuffer.vkCmdBindDescriptorSets( | 533 commandBuffer.vkCmdBindDescriptorSets( |
| 506 VK_PIPELINE_BIND_POINT_GRAPHICS, | 534 VK_PIPELINE_BIND_POINT_GRAPHICS, |
| 507 pipeline.layout, | 535 pipeline.layout, |
| 508 0, | 536 0, |
| 509 pipeline.descriptorSets[currentFrameInFlight].len, | 537 pipeline.descriptorSets[currentFrameInFlight].len.uint32, |
| 510 pipeline.descriptorSets[currentFrameInFlight], | 538 pipeline.descriptorSets[currentFrameInFlight].ToCPointer, |
| 511 0, | 539 0, |
| 512 nil, | 540 nil, |
| 513 ) | 541 ) |
| 514 | 542 |
| 515 proc AssertCompatible(TShader, TMesh, TInstance, TGlobals: typedesc) = | 543 proc AssertCompatible(TShader, TMesh, TInstance, TGlobals: typedesc) = |
| 516 # assert seq-fields of TMesh|TInstance == seq-fields of TShader | 544 # assert seq-fields of TMesh|TInstance == seq-fields of TShader |
| 517 # assert normal fields of TMesh|Globals == normal fields of TShaderDescriptors | 545 # assert normal fields of TMesh|Globals == normal fields of TShaderDescriptors |
| 518 for inputName, inputValue in default(TShader).fieldPairs: | 546 for inputName, inputValue in default(TShader).fieldPairs: |
| 519 echo "checking shader input '" & inputName & "'" | |
| 520 var foundField = false | 547 var foundField = false |
| 521 when hasCustomPragma(inputValue, VertexAttribute): | 548 when hasCustomPragma(inputValue, VertexAttribute): |
| 522 echo " is vertex attribute" | 549 assert typeof(inputValue) is SupportedGPUType |
| 523 for meshName, meshValue in default(TMesh).fieldPairs: | 550 for meshName, meshValue in default(TMesh).fieldPairs: |
| 524 when meshName == inputName: | 551 when meshName == inputName: |
| 525 assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" | 552 assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" |
| 526 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)) & "'" | 553 assert elementType(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(elementType(meshValue)) & "'" |
| 527 foundField = true | 554 foundField = true |
| 528 assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TMesh) & "'" | 555 assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TMesh) & "'" |
| 529 elif hasCustomPragma(inputValue, InstanceAttribute): | 556 elif hasCustomPragma(inputValue, InstanceAttribute): |
| 530 echo " is instance attribute" | 557 assert typeof(inputValue) is SupportedGPUType |
| 531 for instanceName, instanceValue in default(TInstance).fieldPairs: | 558 for instanceName, instanceValue in default(TInstance).fieldPairs: |
| 532 when instanceName == inputName: | 559 when instanceName == inputName: |
| 533 assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" | 560 assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" |
| 534 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)) & "'" | 561 assert elementType(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(elementType(instanceValue)) & "'" |
| 535 foundField = true | 562 foundField = true |
| 536 assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TInstance) & "'" | 563 assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TInstance) & "'" |
| 537 elif hasCustomPragma(inputValue, Descriptor): | 564 elif typeof(inputValue) is Texture or typeof(inputValue) is object: |
| 538 echo " is descriptor attribute" | |
| 539 for meshName, meshValue in default(TMesh).fieldPairs: | 565 for meshName, meshValue in default(TMesh).fieldPairs: |
| 540 when meshName == inputName: | 566 when meshName == inputName: |
| 541 assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" | 567 assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" |
| 542 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)) & "'" | 568 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(elementType(meshValue)) & "'" |
| 543 foundField = true | 569 foundField = true |
| 544 for globalName, globalValue in default(TGlobals).fieldPairs: | 570 for globalName, globalValue in default(TGlobals).fieldPairs: |
| 545 when globalName == inputName: | 571 when globalName == inputName: |
| 546 assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" | 572 assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" |
| 547 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)) & "'" | 573 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)) & "'" |
| 548 foundField = true | 574 foundField = true |
| 549 assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TMesh) & "|" & tt.name(TGlobals) & "'" | 575 assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TMesh) & "|" & tt.name(TGlobals) & "'" |
| 550 echo " found" | 576 elif typeof(inputValue) is array: |
| 577 when (elementType(inputValue) is Texture or elementType(inputValue) is object): | |
| 578 for meshName, meshValue in default(TMesh).fieldPairs: | |
| 579 when meshName == inputName: | |
| 580 assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" | |
| 581 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(elementType(meshValue)) & "'" | |
| 582 foundField = true | |
| 583 for globalName, globalValue in default(TGlobals).fieldPairs: | |
| 584 when globalName == inputName: | |
| 585 assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" | |
| 586 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)) & "'" | |
| 587 foundField = true | |
| 588 assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TMesh) & "|" & tt.name(TGlobals) & "'" | |
| 551 | 589 |
| 552 | 590 |
| 553 proc Render[TShader, TMesh, TInstance, TGlobals]( | 591 proc Render[TShader, TMesh, TInstance, TGlobals]( |
| 554 pipeline: Pipeline[TShader], | 592 pipeline: Pipeline[TShader], |
| 555 renderable: Renderable[TMesh, TInstance], | 593 renderable: Renderable[TMesh, TInstance], |
| 585 firstInstance = 0 | 623 firstInstance = 0 |
| 586 ) | 624 ) |
| 587 | 625 |
| 588 when isMainModule: | 626 when isMainModule: |
| 589 import semicongine/platform/window | 627 import semicongine/platform/window |
| 590 import semicongine/core/vulkanapi | |
| 591 import semicongine/vulkan/instance | 628 import semicongine/vulkan/instance |
| 592 import semicongine/vulkan/device | 629 import semicongine/vulkan/device |
| 593 import semicongine/vulkan/physicaldevice | 630 import semicongine/vulkan/physicaldevice |
| 594 import semicongine/vulkan/renderpass | 631 import semicongine/vulkan/renderpass |
| 632 import semicongine/vulkan/commandbuffer | |
| 633 import std/options | |
| 595 | 634 |
| 596 type | 635 type |
| 636 MaterialA = object | |
| 637 reflection: float32 | |
| 638 baseColor: Vec3f | |
| 639 ShaderSettings = object | |
| 640 brightness: float32 | |
| 597 MeshA = object | 641 MeshA = object |
| 598 position: seq[Vec3f] | 642 position: seq[Vec3f] |
| 599 transparency: float | 643 transparency: float |
| 644 material: array[3, MaterialA] | |
| 645 materialTextures: array[3, Texture] | |
| 600 InstanceA = object | 646 InstanceA = object |
| 601 transform: seq[Mat4] | 647 transform: seq[Mat4] |
| 602 position: seq[Vec3f] | 648 position: seq[Vec3f] |
| 603 other: seq[array[3, int32]] | |
| 604 Globals = object | 649 Globals = object |
| 605 fontAtlas: Texture | 650 fontAtlas: Texture |
| 651 settings: ShaderSettings | |
| 606 | 652 |
| 607 ShaderA = object | 653 ShaderA = object |
| 654 # vertex input | |
| 608 position {.VertexAttribute.}: Vec3f | 655 position {.VertexAttribute.}: Vec3f |
| 609 transform {.InstanceAttribute.}: Mat4 | 656 transform {.InstanceAttribute.}: Mat4 |
| 610 fontAtlas {.Descriptor.}: Texture | 657 # intermediate |
| 611 other {.InstanceAttribute.}: array[3, int32] | |
| 612 test {.Pass.}: float32 | 658 test {.Pass.}: float32 |
| 613 test1 {.PassFlat.}: Vec3f | 659 test1 {.PassFlat.}: Vec3f |
| 660 # output | |
| 614 color {.ShaderOutput.}: Vec4f | 661 color {.ShaderOutput.}: Vec4f |
| 662 # uniforms | |
| 663 material: array[3, MaterialA] | |
| 664 settings: ShaderSettings | |
| 665 # textures | |
| 666 fontAtlas: Texture | |
| 667 materialTextures: array[3, Texture] | |
| 668 # code | |
| 615 vertexCode: string = "void main() {}" | 669 vertexCode: string = "void main() {}" |
| 616 fragmentCode: string = "void main() {}" | 670 fragmentCode: string = "void main() {}" |
| 617 | 671 |
| 618 let w = CreateWindow("test2") | 672 let w = CreateWindow("test2") |
| 619 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") | 673 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") |
| 621 vulkanVersion = VK_MAKE_API_VERSION(0, 1, 3, 0), | 675 vulkanVersion = VK_MAKE_API_VERSION(0, 1, 3, 0), |
| 622 instanceExtensions = @[], | 676 instanceExtensions = @[], |
| 623 layers = @["VK_LAYER_KHRONOS_validation"], | 677 layers = @["VK_LAYER_KHRONOS_validation"], |
| 624 ) | 678 ) |
| 625 | 679 |
| 626 const (a, b) = CompileShader(Shader(A)) | |
| 627 | 680 |
| 628 let selectedPhysicalDevice = i.GetPhysicalDevices().FilterBestGraphics() | 681 let selectedPhysicalDevice = i.GetPhysicalDevices().FilterBestGraphics() |
| 629 let d = i.CreateDevice( | 682 let d = i.CreateDevice( |
| 630 selectedPhysicalDevice, | 683 selectedPhysicalDevice, |
| 631 enabledExtensions = @[], | 684 enabledExtensions = @[], |
| 632 selectedPhysicalDevice.FilterForGraphicsPresentationQueues() | 685 selectedPhysicalDevice.FilterForGraphicsPresentationQueues() |
| 633 ) | 686 ) |
| 634 | 687 |
| 635 var p: Pipeline[ShaderA] | |
| 636 var r: Renderable[MeshA, InstanceA] | 688 var r: Renderable[MeshA, InstanceA] |
| 637 var g: Globals | 689 var g: Globals |
| 638 | 690 |
| 639 let rp = CreateRenderPass(d.vk, d.physicalDevice.GetSurfaceFormats().FilterSurfaceFormat().format) | 691 const shader = ShaderA() |
| 640 var p1 = CreatePipeline[ShaderA](device = d.vk, renderPass = rp, VkShaderModule(0), VkShaderModule(0)) | 692 let shaderObject = d.vk.CompileShader(shader) |
| 641 Render(p, r, g, VkCommandBuffer(0)) | 693 let rp = d.vk.CreateRenderPass(d.physicalDevice.GetSurfaceFormats().FilterSurfaceFormat().format) |
| 694 var p = CreatePipeline(d.vk, renderPass = rp, shaderObject) | |
| 695 | |
| 696 let commandBufferPool = d.CreateCommandBufferPool(d.FirstGraphicsQueue().get().family, INFLIGHTFRAMES) | |
| 697 let cmd = commandBufferPool.buffers[0] | |
| 698 | |
| 699 checkVkResult cmd.vkResetCommandBuffer(VkCommandBufferResetFlags(0)) | |
| 700 let beginInfo = VkCommandBufferBeginInfo( | |
| 701 sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, | |
| 702 flags: VkCommandBufferUsageFlags(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT), | |
| 703 ) | |
| 704 checkVkResult cmd.vkBeginCommandBuffer(addr(beginInfo)) | |
| 705 p.Bind(cmd, currentFrameInFlight = 0) | |
| 706 p.Render(r, g, cmd) | |
| 707 | |
| 708 checkVkResult cmd.vkEndCommandBuffer() |
