Mercurial > games > semicongine
comparison static_utils.nim @ 1162:46fae89cffb0 compiletime-tests
advance evenmore!
| author | sam <sam@basx.dev> |
|---|---|
| date | Thu, 20 Jun 2024 09:37:44 +0700 |
| parents | dbca0528c714 |
| children | 438d32d8b14f |
comparison
equal
deleted
inserted
replaced
| 1161:dbca0528c714 | 1162:46fae89cffb0 |
|---|---|
| 1 import std/os | |
| 1 import std/macros | 2 import std/macros |
| 2 import std/strformat | 3 import std/strformat |
| 3 import std/typetraits | 4 import std/typetraits as tt |
| 4 | 5 |
| 5 import semicongine/core/utils | 6 import semicongine/core/utils |
| 6 import semicongine/core/imagetypes | 7 import semicongine/core/imagetypes |
| 7 import semicongine/core/vector | 8 import semicongine/core/vector |
| 8 import semicongine/core/matrix | 9 import semicongine/core/matrix |
| 9 import semicongine/core/vulkanapi | 10 import semicongine/core/vulkanapi |
| 10 import semicongine/vulkan/buffer | 11 import semicongine/vulkan/buffer |
| 11 | 12 |
| 12 template VertexAttribute* {.pragma.} | 13 template VertexAttribute* {.pragma.} |
| 13 template InstanceAttribute* {.pragma.} | 14 template InstanceAttribute* {.pragma.} |
| 14 template DescriptorAttribute* {.pragma.} | 15 template Descriptor* {.pragma.} |
| 15 | 16 template Pass* {.pragma.} |
| 17 template PassFlat* {.pragma.} | |
| 18 template ShaderOutput* {.pragma.} | |
| 16 | 19 |
| 17 type | 20 type |
| 18 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] | 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] |
| 19 | 22 |
| 20 func VkType[T: SupportedGPUType](value: T): VkFormat = | 23 func VkType[T: SupportedGPUType](value: T): VkFormat = |
| 44 elif T is TVec2[float64]: VK_FORMAT_R64G64_SFLOAT | 47 elif T is TVec2[float64]: VK_FORMAT_R64G64_SFLOAT |
| 45 elif T is TVec3[float32]: VK_FORMAT_R32G32B32_SFLOAT | 48 elif T is TVec3[float32]: VK_FORMAT_R32G32B32_SFLOAT |
| 46 elif T is TVec3[float64]: VK_FORMAT_R64G64B64_SFLOAT | 49 elif T is TVec3[float64]: VK_FORMAT_R64G64B64_SFLOAT |
| 47 elif T is TVec4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT | 50 elif T is TVec4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT |
| 48 elif T is TVec4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT | 51 elif T is TVec4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT |
| 49 elif T is Mat2[float32]: VK_FORMAT_R32G32_SFLOAT | 52 elif T is TMat2[float32]: VK_FORMAT_R32G32_SFLOAT |
| 50 elif T is Mat2[float64]: VK_FORMAT_R64G64_SFLOAT | 53 elif T is TMat2[float64]: VK_FORMAT_R64G64_SFLOAT |
| 51 elif T is Mat23[float32]: VK_FORMAT_R32G32B32_SFLOAT | 54 elif T is TMat23[float32]: VK_FORMAT_R32G32B32_SFLOAT |
| 52 elif T is Mat23[float64]: VK_FORMAT_R64G64B64_SFLOAT | 55 elif T is TMat23[float64]: VK_FORMAT_R64G64B64_SFLOAT |
| 53 elif T is Mat32[float32]: VK_FORMAT_R32G32_SFLOAT | 56 elif T is TMat32[float32]: VK_FORMAT_R32G32_SFLOAT |
| 54 elif T is Mat32[float64]: VK_FORMAT_R64G64_SFLOAT | 57 elif T is TMat32[float64]: VK_FORMAT_R64G64_SFLOAT |
| 55 elif T is Mat3[float32]: VK_FORMAT_R32G32B32_SFLOAT | 58 elif T is TMat3[float32]: VK_FORMAT_R32G32B32_SFLOAT |
| 56 elif T is Mat3[float64]: VK_FORMAT_R64G64B64_SFLOAT | 59 elif T is TMat3[float64]: VK_FORMAT_R64G64B64_SFLOAT |
| 57 elif T is Mat34[float32]: VK_FORMAT_R32G32B32A32_SFLOAT | 60 elif T is TMat34[float32]: VK_FORMAT_R32G32B32A32_SFLOAT |
| 58 elif T is Mat34[float64]: VK_FORMAT_R64G64B64A64_SFLOAT | 61 elif T is TMat34[float64]: VK_FORMAT_R64G64B64A64_SFLOAT |
| 59 elif T is Mat43[float32]: VK_FORMAT_R32G32B32_SFLOAT | 62 elif T is TMat43[float32]: VK_FORMAT_R32G32B32_SFLOAT |
| 60 elif T is Mat43[float64]: VK_FORMAT_R64G64B64_SFLOAT | 63 elif T is TMat43[float64]: VK_FORMAT_R64G64B64_SFLOAT |
| 61 elif T is Mat4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT | 64 elif T is TMat4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT |
| 62 elif T is Mat4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT | 65 elif T is TMat4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT |
| 66 else: {.error: "Unsupported data type on GPU".} | |
| 67 | |
| 68 func GlslType[T: SupportedGPUType](value: T): string = | |
| 69 when T is float32: "float" | |
| 70 elif T is float64: "double" | |
| 71 elif T is int8, int16, int32, int64: "int" | |
| 72 elif T is uint8, uint16, uint32, uint64: "uint" | |
| 73 elif T is TVec2[int32]: "ivec2" | |
| 74 elif T is TVec2[int64]: "ivec2" | |
| 75 elif T is TVec3[int32]: "ivec3" | |
| 76 elif T is TVec3[int64]: "ivec3" | |
| 77 elif T is TVec4[int32]: "ivec4" | |
| 78 elif T is TVec4[int64]: "ivec4" | |
| 79 elif T is TVec2[uint32]: "uvec2" | |
| 80 elif T is TVec2[uint64]: "uvec2" | |
| 81 elif T is TVec3[uint32]: "uvec3" | |
| 82 elif T is TVec3[uint64]: "uvec3" | |
| 83 elif T is TVec4[uint32]: "uvec4" | |
| 84 elif T is TVec4[uint64]: "uvec4" | |
| 85 elif T is TVec2[float32]: "vec2" | |
| 86 elif T is TVec2[float64]: "dvec2" | |
| 87 elif T is TVec3[float32]: "vec3" | |
| 88 elif T is TVec3[float64]: "dvec3" | |
| 89 elif T is TVec4[float32]: "vec4" | |
| 90 elif T is TVec4[float64]: "dvec4" | |
| 91 elif T is TMat2[float32]: "mat2" | |
| 92 elif T is TMat2[float64]: "dmat2" | |
| 93 elif T is TMat23F32]: "mat23" | |
| 94 elif T is TMat23[float64]: "dmat23" | |
| 95 elif T is TMat32[float32]: "mat32" | |
| 96 elif T is TMat32[float64]: "dmat32" | |
| 97 elif T is TMat3[float32]: "mat3" | |
| 98 elif T is TMat3[float64]: "dmat3" | |
| 99 elif T is TMat34[float32]: "mat34" | |
| 100 elif T is TMat34[float64]: "dmat34" | |
| 101 elif T is TMat43[float32]: "mat43" | |
| 102 elif T is TMat43[float64]: "dmat43" | |
| 103 elif T is TMat4[float32]: "mat4" | |
| 104 elif T is TMat4[float64]: "dmat4" | |
| 105 elif T is Texture: "sampler2D" | |
| 63 else: {.error: "Unsupported data type on GPU".} | 106 else: {.error: "Unsupported data type on GPU".} |
| 64 | 107 |
| 65 template getElementType(field: typed): untyped = | 108 template getElementType(field: typed): untyped = |
| 66 when not (typeof(field) is seq or typeof(field) is array): | 109 when not (typeof(field) is seq or typeof(field) is array): |
| 67 {.error: "getElementType can only be used with seq or array".} | 110 typeof(field) |
| 68 genericParams(typeof(field)).get(0) | 111 # {.error: "getElementType can only be used with seq or array".} |
| 112 else: | |
| 113 genericParams(typeof(field)).get(0) | |
| 69 | 114 |
| 70 template ForVertexDataFields*(inputData: typed, fieldname, valuename, isinstancename, body: untyped): untyped = | 115 template ForVertexDataFields*(inputData: typed, fieldname, valuename, isinstancename, body: untyped): untyped = |
| 71 for theFieldname, value in fieldPairs(inputData): | 116 for theFieldname, value in fieldPairs(inputData): |
| 72 when hasCustomPragma(value, VertexAttribute) or hasCustomPragma(value, InstanceAttribute): | 117 when hasCustomPragma(value, VertexAttribute) or hasCustomPragma(value, InstanceAttribute): |
| 73 when not typeof(value) is seq: | 118 when not typeof(value) is seq: |
| 74 {.error: "field '" & theFieldname & "' needs to be a seq".} | 119 {.error: "field '" & theFieldname & "' needs to be a seq".} |
| 75 when not typeof(value) is SupportedGPUType: | 120 when not typeof(value) is SupportedGPUType: |
| 76 {.error: "field '" & theFieldname & "' is not a supported GPU type".} | 121 {.error: "field '" & theFieldname & "' is not a supported GPU type".} |
| 77 block: | 122 block: |
| 78 let `fieldname` {.inject.} = theFieldname | 123 let `fieldname` {.inject.} = theFieldname |
| 79 let `valuename` {.inject.} = default(getElementType(value)) | 124 let `valuename` {.inject.} = value |
| 80 let `isinstancename` {.inject.} = value.isInstanceAttribute() | 125 let `isinstancename` {.inject.} = hasCustomPragma(value, InstanceAttribute) |
| 81 body | 126 body |
| 82 | 127 |
| 83 template ForDescriptorFields*(inputData: typed, fieldname, valuename, typename, countname, body: untyped): untyped = | 128 template ForDescriptorFields*(inputData: typed, fieldname, valuename, typename, countname, body: untyped): untyped = |
| 84 for theFieldname, value in fieldPairs(inputData): | 129 for theFieldname, value in fieldPairs(inputData): |
| 85 when hasCustomPragma(value, DescriptorAttribute): | 130 when hasCustomPragma(value, Descriptor): |
| 86 when not ( | 131 when not ( |
| 87 typeof(value) is SupportedGPUType | 132 typeof(value) is SupportedGPUType or |
| 88 or (typeof(value) is array and elementType(value) is SupportedGPUType) | 133 typeof(value) is Texture or |
| 89 or typeof(value) is Texture | 134 (typeof(value) is array and getElementType(value) is SupportedGPUType) |
| 90 ): | 135 ): |
| 91 {.error: "field '" & theFieldname & "' needs to be a SupportedGPUType or an array of SupportedGPUType".} | 136 {.error: "field '" & theFieldname & "' needs to be a SupportedGPUType or an array of SupportedGPUType or a Texture".} |
| 92 block: | 137 block: |
| 93 let `fieldname` {.inject.} = theFieldname | 138 let `fieldname` {.inject.} = theFieldname |
| 94 let `valuename` {.inject.} = default(getElementType(value)) | 139 let `valuename` {.inject.} = default(getElementType(value)) |
| 95 | 140 |
| 96 # TODO | 141 when typeof(value) is Texture or (typeof(value) is array and getElementType(value) is Texture): |
| 97 let `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER | 142 let `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER |
| 98 let `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER | |
| 99 | |
| 100 when typeof(value) is array: | |
| 101 let `countname` {.inject.} = genericParams(typeof(value)).get(0) | |
| 102 else: | 143 else: |
| 103 let `countname` {.inject.} = 1 | 144 let `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER |
| 145 | |
| 146 when typeof(value) is SupportedGPUType or typeof(value) is Texture: | |
| 147 let `countname` {.inject.} = 1'u32 | |
| 148 else: | |
| 149 assert typeof(value) is array | |
| 150 let `countname` {.inject.} = uint32(genericParams(typeof(value)).get(0)) | |
| 104 body | 151 body |
| 105 | 152 |
| 106 func NumberOfVertexInputAttributeDescriptors[T: SupportedGPUType](value: T): uint32 = | 153 func NumberOfVertexInputAttributeDescriptors[T: SupportedGPUType](value: T): uint32 = |
| 107 when T is TMat2[float32] or T is TMat2[float64] or T is TMat23[float32] or T is TMat23[float64]: | 154 when T is TMat2[float32] or T is TMat2[float64] or T is TMat23[float32] or T is TMat23[float64]: |
| 108 2 | 155 2 |
| 111 elif T is TMat43[float32] or T is TMat43[float64] or T is TMat4[float32] or T is TMat4[float64]: | 158 elif T is TMat43[float32] or T is TMat43[float64] or T is TMat4[float32] or T is TMat4[float64]: |
| 112 4 | 159 4 |
| 113 else: | 160 else: |
| 114 1 | 161 1 |
| 115 | 162 |
| 116 func NLocationSlots[T: SupportedGPUType](value: T): uint32 = | 163 func NLocationSlots[T: SupportedGPUType|Texture](value: T): uint32 = |
| 117 #[ | 164 #[ |
| 118 single location: | 165 single location: |
| 166 - any scalar | |
| 167 - any 16-bit vector | |
| 168 - any 32-bit vector | |
| 169 - any 64-bit vector that has max. 2 components | |
| 119 16-bit scalar and vector types, and | 170 16-bit scalar and vector types, and |
| 120 32-bit scalar and vector types, and | 171 32-bit scalar and vector types, and |
| 121 64-bit scalar and 2-component vector types. | 172 64-bit scalar and 2-component vector types. |
| 122 two locations | 173 two locations |
| 123 64-bit three- and four-component vectors | 174 64-bit three- and four-component vectors |
| 124 ]# | 175 ]# |
| 125 when typeof(value) is TVec3 and sizeof(getElementType(value)) == 8: | 176 when T is TVec3[int64] or |
| 126 return 2 | 177 T is TVec4[int64] or |
| 127 elif typeof(value) is TVec4 and sizeof(getElementType(value)) == 8: | 178 T is TVec3[uint64] or |
| 179 T is TVec4[uint64] or | |
| 180 T is TVec3[float64] or | |
| 181 T is TVec4[float64] or | |
| 182 T is TMat23[float64] or | |
| 183 T is TMat3[float64] or | |
| 184 T is TMat34[float64] or | |
| 185 T is TMat43[float64] or | |
| 186 T is TMat4[float64]: | |
| 128 return 2 | 187 return 2 |
| 129 else: | 188 else: |
| 130 return 1 | 189 return 1 |
| 131 | 190 |
| 132 type | 191 type |
| 144 vertexCount: uint32 | 203 vertexCount: uint32 |
| 145 else: | 204 else: |
| 146 indexBuffer: VkBuffer | 205 indexBuffer: VkBuffer |
| 147 indexCount: uint32 | 206 indexCount: uint32 |
| 148 indexBufferOffset: VkDeviceSize | 207 indexBufferOffset: VkDeviceSize |
| 149 Pipeline[TShaderInputs] = object | 208 Pipeline[TShader] = object |
| 150 pipeline: VkPipeline | 209 pipeline: VkPipeline |
| 151 layout: VkPipelineLayout | 210 layout: VkPipelineLayout |
| 152 descriptorSets: array[2, seq[VkDescriptorSet]] | 211 descriptorSets: array[2, seq[VkDescriptorSet]] |
| 153 ShaderSet[TShaderInputs] = object | 212 |
| 154 vertexShader: VkShaderModule | |
| 155 fragmentShader: VkShaderModule | |
| 156 converter toVkIndexType(indexType: IndexType): VkIndexType = | 213 converter toVkIndexType(indexType: IndexType): VkIndexType = |
| 157 case indexType: | 214 case indexType: |
| 158 of None: VK_INDEX_TYPE_NONE_KHR | 215 of None: VK_INDEX_TYPE_NONE_KHR |
| 159 of UInt8: VK_INDEX_TYPE_UINT8_EXT | 216 of UInt8: VK_INDEX_TYPE_UINT8_EXT |
| 160 of UInt16: VK_INDEX_TYPE_UINT16 | 217 of UInt16: VK_INDEX_TYPE_UINT16 |
| 161 of UInt32: VK_INDEX_TYPE_UINT32 | 218 of UInt32: VK_INDEX_TYPE_UINT32 |
| 162 | 219 |
| 163 | 220 proc compileGlslToSPIRV(stage: VkShaderStageFlagBits, shaderSource: string): seq[uint32] {.compileTime.} = |
| 164 proc CreatePipeline*[TShaderInputs]( | 221 func stage2string(stage: VkShaderStageFlagBits): string {.compileTime.} = |
| 222 case stage | |
| 223 of VK_SHADER_STAGE_VERTEX_BIT: "vert" | |
| 224 of VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: "tesc" | |
| 225 of VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: "tese" | |
| 226 of VK_SHADER_STAGE_GEOMETRY_BIT: "geom" | |
| 227 of VK_SHADER_STAGE_FRAGMENT_BIT: "frag" | |
| 228 of VK_SHADER_STAGE_COMPUTE_BIT: "comp" | |
| 229 else: "" | |
| 230 | |
| 231 when defined(nimcheck): # will not run if nimcheck is running | |
| 232 return result | |
| 233 | |
| 234 let | |
| 235 stagename = stage2string(stage) | |
| 236 shaderHash = hash(shaderSource) | |
| 237 shaderfile = getTempDir() / &"shader_{shaderHash}.{stagename}" | |
| 238 | |
| 239 | |
| 240 if not shaderfile.fileExists: | |
| 241 echo "shader of type ", stage, ", entrypoint ", entrypoint | |
| 242 for i, line in enumerate(shaderSource.splitlines()): | |
| 243 echo " ", i + 1, " ", line | |
| 244 var glslExe = currentSourcePath.parentDir.parentDir.parentDir / "tools" / "glslangValidator" | |
| 245 when defined(windows): | |
| 246 glslExe = glslExe & "." & ExeExt | |
| 247 let command = &"{glslExe} --entry-point {entrypoint} -V --stdin -S {stagename} -o {shaderfile}" | |
| 248 echo "run: ", command | |
| 249 discard StaticExecChecked( | |
| 250 command = command, | |
| 251 input = shaderSource | |
| 252 ) | |
| 253 else: | |
| 254 echo &"shaderfile {shaderfile} is up-to-date" | |
| 255 | |
| 256 when defined(mingw) and defined(linux): # required for crosscompilation, path separators get messed up | |
| 257 let shaderbinary = staticRead shaderfile.replace("\\", "/") | |
| 258 else: | |
| 259 let shaderbinary = staticRead shaderfile | |
| 260 | |
| 261 var i = 0 | |
| 262 while i < shaderbinary.len: | |
| 263 result.add( | |
| 264 (uint32(shaderbinary[i + 0]) shl 0) or | |
| 265 (uint32(shaderbinary[i + 1]) shl 8) or | |
| 266 (uint32(shaderbinary[i + 2]) shl 16) or | |
| 267 (uint32(shaderbinary[i + 3]) shl 24) | |
| 268 ) | |
| 269 i += 4 | |
| 270 | |
| 271 proc generateShaderSource[TShader](shader: TShader): (string, string) {.compileTime.} = | |
| 272 const GLSL_VERSION = "450" | |
| 273 var vsInput: seq[string] | |
| 274 var vsOutput: seq[string] | |
| 275 var fsInput: seq[string] | |
| 276 var fsOutput: seq[string] | |
| 277 var uniforms: seq[string] | |
| 278 var samplers: seq[string] | |
| 279 var vsInputLocation = 0 | |
| 280 var passLocation = 0 | |
| 281 var fsOutputLocation = 0 | |
| 282 var binding = 0 | |
| 283 | |
| 284 for fieldname, value in fieldPairs(shader): | |
| 285 # vertex shader inputs | |
| 286 if hasCustomPragma(value, VertexAttribute) or hasCustomPragma(value, InstanceAttribute): | |
| 287 assert typeof(value) is SupportedGPUType | |
| 288 vsInput.add &"layout(location = {vsInputLocation}) in {GlslType(value)} {fieldname};" | |
| 289 for j in 0 ..< NumberOfVertexInputAttributeDescriptors(value): | |
| 290 vsInputLocation += NLocationSlots(value) | |
| 291 # intermediate values, passed between shaders | |
| 292 if hasCustomPragma(value, Pass) or hasCustomPragma(value, PassFlat): | |
| 293 let flat = if hasCustomPragma(value, PassFlat): "flat " else "" | |
| 294 vsOutput.add &"layout(location = {passLocation}) {flat}out {GlslType(value)} {fieldname};" | |
| 295 fsInput.add &"layout(location = {passLocation}) {flat}in {GlslType(value)} {fieldname};" | |
| 296 passLocation.inc | |
| 297 if hasCustomPragma(value, ShaderOutput): | |
| 298 fsOutput.add &"layout(location = {fsOutputLocation}) out {GlslType(value)} {fieldname};" | |
| 299 fsOutputLocation.inc | |
| 300 if hasCustomPragma(value, Descriptor): | |
| 301 # TODO; samplers and uniforms | |
| 302 if typeof(value) is Texture: | |
| 303 else: | |
| 304 | |
| 305 result[0] = (@[&"#version {GLSL_VERSION}", "#extension GL_EXT_scalar_block_layout : require", ""] & | |
| 306 vsInput & | |
| 307 uniforms & | |
| 308 samplers & | |
| 309 vsOutput & | |
| 310 @[shader.vertexCode]).join("\n") | |
| 311 | |
| 312 result[1] = (@[&"#version {GLSL_VERSION}", "#extension GL_EXT_scalar_block_layout : require", ""] & | |
| 313 fsInput & | |
| 314 uniforms & | |
| 315 samplers & | |
| 316 fsOutput & | |
| 317 @[shader.fragmentCode]).join("\n") | |
| 318 | |
| 319 proc CompileShader[TShader](shader: TShader): (seq[uint32], seq[uint32]) {.compileTime.} = | |
| 320 let (vertexShaderSource, fragmentShaderSource) = generateShaderSource(shader) | |
| 321 ( | |
| 322 compileGlslToSPIRV(VK_SHADER_STAGE_VERTEX_BIT, vertexShaderSource), | |
| 323 compileGlslToSPIRV(VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShaderSource) | |
| 324 ) | |
| 325 | |
| 326 | |
| 327 proc CreatePipeline*[TShader]( | |
| 165 device: VkDevice, | 328 device: VkDevice, |
| 166 renderPass: VkRenderPass, | 329 renderPass: VkRenderPass, |
| 167 shaderSet: ShaderSet[TShaderInputs], | 330 vertexShader: VkShaderModule, |
| 331 fragmentShader: VkShaderModule, | |
| 168 topology: VkPrimitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, | 332 topology: VkPrimitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, |
| 169 polygonMode: VkPolygonMode = VK_POLYGON_MODE_FILL, | 333 polygonMode: VkPolygonMode = VK_POLYGON_MODE_FILL, |
| 170 cullMode: VkCullModeFlagBits = VK_CULL_MODE_BACK_BIT, | 334 cullMode: VkCullModeFlagBits = VK_CULL_MODE_BACK_BIT, |
| 171 frontFace: VkFrontFace = VK_FRONT_FACE_CLOCKWISE, | 335 frontFace: VkFrontFace = VK_FRONT_FACE_CLOCKWISE, |
| 172 ): Pipeline[TShaderInputs] = | 336 ): Pipeline[TShader] = |
| 173 # assumptions/limitations: | 337 # assumptions/limitations: |
| 174 # - we are only using vertex and fragment shaders (2 stages) | 338 # - we are only using vertex and fragment shaders (2 stages) |
| 175 # - we only support one subpass | 339 # - we only support one subpass |
| 176 | 340 # = we only support one Uniform-Block |
| 177 # CONTINUE HERE, WITH PIPELINE LAYOUT!!!! | |
| 178 # Rely on TShaderInputs | |
| 179 | 341 |
| 180 var layoutbindings: seq[VkDescriptorSetLayoutBinding] | 342 var layoutbindings: seq[VkDescriptorSetLayoutBinding] |
| 181 let descriptors = [ | |
| 182 (VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1), # more than 1 for arrays | |
| 183 (VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1), | |
| 184 ] | |
| 185 var descriptorBindingNumber = 0'u32 | 343 var descriptorBindingNumber = 0'u32 |
| 186 ForDescriptorFields(default(TShaderInputs), fieldname, value, descriptorCount): | 344 ForDescriptorFields(default(TShader), fieldname, value, descriptorType, descriptorCount): |
| 345 # TODO: Only one binding needed for a Uniforms block | |
| 187 layoutbindings.add VkDescriptorSetLayoutBinding( | 346 layoutbindings.add VkDescriptorSetLayoutBinding( |
| 188 binding: descriptorBindingNumber, | 347 binding: descriptorBindingNumber, |
| 189 descriptorType: descriptorType, | 348 descriptorType: descriptorType, |
| 190 descriptorCount: descriptorCount, | 349 descriptorCount: descriptorCount, |
| 191 stageFlags: VK_SHADER_STAGE_ALL_GRAPHICS, | 350 stageFlags: VkShaderStageFlags(VK_SHADER_STAGE_ALL_GRAPHICS), |
| 192 pImmutableSamplers: nil, | 351 pImmutableSamplers: nil, |
| 193 ) | 352 ) |
| 194 inc descriptorBindingNumber | 353 inc descriptorBindingNumber |
| 195 var layoutCreateInfo = VkDescriptorSetLayoutCreateInfo( | 354 var layoutCreateInfo = VkDescriptorSetLayoutCreateInfo( |
| 196 sType: VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, | 355 sType: VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, |
| 197 bindingCount: uint32(layoutbindings.len), | 356 bindingCount: uint32(layoutbindings.len), |
| 198 pBindings: layoutbindings.ToCPointer | 357 pBindings: layoutbindings.ToCPointer |
| 199 ) | 358 ) |
| 200 var descriptorSetLayout: VkDescriptorSetLayout | 359 var descriptorSetLayout: VkDescriptorSetLayout |
| 201 checkVkResult vkCreateDescriptorSetLayout(device.vk, addr(layoutCreateInfo), nil, addr(descriptorSetLayout)) | 360 checkVkResult vkCreateDescriptorSetLayout(device, addr(layoutCreateInfo), nil, addr(descriptorSetLayout)) |
| 202 let pipelineLayoutInfo = VkPipelineLayoutCreateInfo( | 361 let pipelineLayoutInfo = VkPipelineLayoutCreateInfo( |
| 203 sType: VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, | 362 sType: VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, |
| 204 setLayoutCount: 1, | 363 setLayoutCount: 1, |
| 205 pSetLayouts: addr(descriptorSetLayout), | 364 pSetLayouts: addr(descriptorSetLayout), |
| 206 # pushConstantRangeCount: uint32(pushConstants.len), | 365 # pushConstantRangeCount: uint32(pushConstants.len), |
| 210 | 369 |
| 211 let stages = [ | 370 let stages = [ |
| 212 VkPipelineShaderStageCreateInfo( | 371 VkPipelineShaderStageCreateInfo( |
| 213 sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | 372 sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
| 214 stage: VK_SHADER_STAGE_VERTEX_BIT, | 373 stage: VK_SHADER_STAGE_VERTEX_BIT, |
| 215 module: shaderSet.vertexShader, | 374 module: vertexShader, |
| 216 pName: "main", | 375 pName: "main", |
| 217 ), | 376 ), |
| 218 VkPipelineShaderStageCreateInfo( | 377 VkPipelineShaderStageCreateInfo( |
| 219 sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | 378 sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
| 220 stage: VK_SHADER_STAGE_FRAGMENT_BIT, | 379 stage: VK_SHADER_STAGE_FRAGMENT_BIT, |
| 221 module: shaderSet.fragmentShader, | 380 module: fragmentShader, |
| 222 pName: "main", | 381 pName: "main", |
| 223 ), | 382 ), |
| 224 ] | 383 ] |
| 225 let | 384 var |
| 226 bindings: var seq[VkVertexInputBindingDescription] | 385 bindings: seq[VkVertexInputBindingDescription] |
| 227 attributes: var seq[VkVertexInputAttributeDescription] | 386 attributes: seq[VkVertexInputAttributeDescription] |
| 228 var inputBindingNumber = 0'u32 | 387 var inputBindingNumber = 0'u32 |
| 229 var inputLocationNumber = 0'u32 | 388 var location = 0'u32 |
| 230 ForVertexDataFields(default(TShaderInputs), fieldname, value, isInstanceAttr): | 389 ForVertexDataFields(default(TShader), fieldname, value, isInstanceAttr): |
| 231 bindings.add VkVertexInputBindingDescription( | 390 bindings.add VkVertexInputBindingDescription( |
| 232 binding: inputBindingNumber, | 391 binding: inputBindingNumber, |
| 233 stride: sizeof(value).uint32, | 392 stride: sizeof(value).uint32, |
| 234 inputRate: if isInstanceAttr: VK_VERTEX_INPUT_RATE_INSTANCE else: VK_VERTEX_INPUT_RATE_VERTEX, | 393 inputRate: if isInstanceAttr: VK_VERTEX_INPUT_RATE_INSTANCE else: VK_VERTEX_INPUT_RATE_VERTEX, |
| 235 ) | 394 ) |
| 236 # allows to submit larger data structures like Mat44, for most other types will be 1 | 395 # allows to submit larger data structures like Mat44, for most other types will be 1 |
| 237 let perDescriptorSize = sizeof(value).uint32 div NumberOfVertexInputAttributeDescriptors(value) | 396 let perDescriptorSize = sizeof(value).uint32 div NumberOfVertexInputAttributeDescriptors(value) |
| 238 for i in 0'u32 ..< NumberOfVertexInputAttributeDescriptors(value): | 397 for i in 0'u32 ..< NumberOfVertexInputAttributeDescriptors(value): |
| 239 attributes.add VkVertexInputAttributeDescription( | 398 attributes.add VkVertexInputAttributeDescription( |
| 240 binding: inputBindingNumber, | 399 binding: inputBindingNumber, |
| 241 inputLocationNumber: inputLocationNumber, | 400 location: location, |
| 242 format: VkType(value), | 401 format: VkType(value), |
| 243 offset: i * perDescriptorSize, | 402 offset: i * perDescriptorSize, |
| 244 ) | 403 ) |
| 245 inputLocationNumber += NLocationSlots(value) | 404 location += NLocationSlots(value) |
| 246 inc inputBindingNumber | 405 inc inputBindingNumber |
| 247 | 406 |
| 248 let | 407 let |
| 249 vertexInputInfo = VkPipelineVertexInputStateCreateInfo( | 408 vertexInputInfo = VkPipelineVertexInputStateCreateInfo( |
| 250 sType: VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, | 409 sType: VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, |
| 308 pDynamicStates: dynamicStates.ToCPointer, | 467 pDynamicStates: dynamicStates.ToCPointer, |
| 309 ) | 468 ) |
| 310 let createInfo = VkGraphicsPipelineCreateInfo( | 469 let createInfo = VkGraphicsPipelineCreateInfo( |
| 311 sType: VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, | 470 sType: VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, |
| 312 stageCount: 2, | 471 stageCount: 2, |
| 313 pStages: addr(stages), | 472 pStages: stages.ToCPointer, |
| 314 pVertexInputState: addr(vertexInputInfo), | 473 pVertexInputState: addr(vertexInputInfo), |
| 315 pInputAssemblyState: addr(inputAssembly), | 474 pInputAssemblyState: addr(inputAssembly), |
| 316 pViewportState: addr(viewportState), | 475 pViewportState: addr(viewportState), |
| 317 pRasterizationState: addr(rasterizer), | 476 pRasterizationState: addr(rasterizer), |
| 318 pMultisampleState: addr(multisampling), | 477 pMultisampleState: addr(multisampling), |
| 351 pipeline.descriptorSets[currentFrameInFlight], | 510 pipeline.descriptorSets[currentFrameInFlight], |
| 352 0, | 511 0, |
| 353 nil, | 512 nil, |
| 354 ) | 513 ) |
| 355 | 514 |
| 356 proc AssertCompatible(TShaderInputs, TMesh, TInstance, TGlobals: typedesc) = | 515 proc AssertCompatible(TShader, TMesh, TInstance, TGlobals: typedesc) = |
| 357 # assert seq-fields of TMesh|TInstance == seq-fields of TShaderInputs | 516 # assert seq-fields of TMesh|TInstance == seq-fields of TShader |
| 358 # assert normal fields of TMesh|Globals == normal fields of TShaderDescriptors | 517 # assert normal fields of TMesh|Globals == normal fields of TShaderDescriptors |
| 359 for inputName, inputValue in default(TShaderInputs).fieldPairs: | 518 for inputName, inputValue in default(TShader).fieldPairs: |
| 360 echo "checking shader input '" & inputName & "'" | 519 echo "checking shader input '" & inputName & "'" |
| 361 var foundField = false | 520 var foundField = false |
| 362 when hasCustomPragma(inputValue, VertexAttribute): | 521 when hasCustomPragma(inputValue, VertexAttribute): |
| 363 echo " is vertex attribute" | 522 echo " is vertex attribute" |
| 364 for meshName, meshValue in default(TMesh).fieldPairs: | 523 for meshName, meshValue in default(TMesh).fieldPairs: |
| 365 when meshName == inputName: | 524 when meshName == inputName: |
| 366 assert foundField == false, "Shader input '" & TShaderInputs.name & "." & inputName & "' has been found more than once" | 525 assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" |
| 367 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 & "'" | 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)) & "'" |
| 368 foundField = true | 527 foundField = true |
| 369 assert foundField, "Shader input '" & TShaderInputs.name & "." & inputName & ": " & typeof(inputValue).name & "' not found in '" & TMesh.name & "'" | 528 assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TMesh) & "'" |
| 370 elif hasCustomPragma(inputValue, InstanceAttribute): | 529 elif hasCustomPragma(inputValue, InstanceAttribute): |
| 371 echo " is instance attribute" | 530 echo " is instance attribute" |
| 372 for instanceName, instanceValue in default(TInstance).fieldPairs: | 531 for instanceName, instanceValue in default(TInstance).fieldPairs: |
| 373 when instanceName == inputName: | 532 when instanceName == inputName: |
| 374 assert foundField == false, "Shader input '" & TShaderInputs.name & "." & inputName & "' has been found more than once" | 533 assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" |
| 375 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 & "'" | 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)) & "'" |
| 376 foundField = true | 535 foundField = true |
| 377 assert foundField, "Shader input '" & TShaderInputs.name & "." & inputName & ": " & typeof(inputValue).name & "' not found in '" & TInstance.name & "'" | 536 assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TInstance) & "'" |
| 378 elif hasCustomPragma(inputValue, DescriptorAttribute): | 537 elif hasCustomPragma(inputValue, Descriptor): |
| 379 echo " is descriptor attribute" | 538 echo " is descriptor attribute" |
| 380 for meshName, meshValue in default(TMesh).fieldPairs: | 539 for meshName, meshValue in default(TMesh).fieldPairs: |
| 381 when meshName == inputName: | 540 when meshName == inputName: |
| 382 assert foundField == false, "Shader input '" & TShaderInputs.name & "." & inputName & "' has been found more than once" | 541 assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" |
| 383 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 & "'" | 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)) & "'" |
| 384 foundField = true | 543 foundField = true |
| 385 for globalName, globalValue in default(TGlobals).fieldPairs: | 544 for globalName, globalValue in default(TGlobals).fieldPairs: |
| 386 when globalName == inputName: | 545 when globalName == inputName: |
| 387 assert foundField == false, "Shader input '" & TShaderInputs.name & "." & inputName & "' has been found more than once" | 546 assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" |
| 388 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 & "'" | 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)) & "'" |
| 389 foundField = true | 548 foundField = true |
| 390 assert foundField, "Shader input '" & TShaderInputs.name & "." & inputName & ": " & typeof(inputValue).name & "' not found in '" & TMesh.name & "|" & TGlobals.name & "'" | 549 assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TMesh) & "|" & tt.name(TGlobals) & "'" |
| 391 echo " found" | 550 echo " found" |
| 392 | 551 |
| 393 | 552 |
| 394 proc Render[TShaderInputs, TMesh, TInstance, TGlobals]( | 553 proc Render[TShader, TMesh, TInstance, TGlobals]( |
| 395 pipeline: Pipeline[TShaderInputs], | 554 pipeline: Pipeline[TShader], |
| 396 renderable: Renderable[TMesh, TInstance], | 555 renderable: Renderable[TMesh, TInstance], |
| 397 globals: TGlobals, | 556 globals: TGlobals, |
| 398 commandBuffer: VkCommandBuffer, | 557 commandBuffer: VkCommandBuffer, |
| 399 ) = | 558 ) = |
| 400 static: | 559 static: |
| 401 AssertCompatible(TShaderInputs, TMesh, TInstance, TGlobals) | 560 AssertCompatible(TShader, TMesh, TInstance, TGlobals) |
| 402 commandBuffer.vkCmdBindVertexBuffers( | 561 commandBuffer.vkCmdBindVertexBuffers( |
| 403 firstBinding = 0'u32, | 562 firstBinding = 0'u32, |
| 404 bindingCount = uint32(renderable.vertexBuffers.len), | 563 bindingCount = uint32(renderable.vertexBuffers.len), |
| 405 pBuffers = renderable.vertexBuffers.ToCPointer(), | 564 pBuffers = renderable.vertexBuffers.ToCPointer(), |
| 406 pOffsets = renderable.bufferOffsets.ToCPointer() | 565 pOffsets = renderable.bufferOffsets.ToCPointer() |
| 425 firstVertex = 0, | 584 firstVertex = 0, |
| 426 firstInstance = 0 | 585 firstInstance = 0 |
| 427 ) | 586 ) |
| 428 | 587 |
| 429 when isMainModule: | 588 when isMainModule: |
| 589 import semicongine/platform/window | |
| 590 import semicongine/core/vulkanapi | |
| 591 import semicongine/vulkan/instance | |
| 592 import semicongine/vulkan/device | |
| 593 import semicongine/vulkan/physicaldevice | |
| 594 import semicongine/vulkan/renderpass | |
| 595 | |
| 430 type | 596 type |
| 431 MeshA = object | 597 MeshA = object |
| 432 position: seq[Vec3f] | 598 position: seq[Vec3f] |
| 433 transparency: float | 599 transparency: float |
| 434 InstanceA = object | 600 InstanceA = object |
| 435 transform: seq[Mat4] | 601 transform: seq[Mat4] |
| 436 position: seq[Vec3f] | 602 position: seq[Vec3f] |
| 603 other: seq[array[3, int32]] | |
| 437 Globals = object | 604 Globals = object |
| 438 color: Vec4f | 605 fontAtlas: Texture |
| 439 | 606 |
| 440 ShaderInputsA = object | 607 ShaderA = object |
| 441 position {.VertexAttribute.}: Vec3f | 608 position {.VertexAttribute.}: Vec3f |
| 442 transform {.InstanceAttribute.}: Mat4 | 609 transform {.InstanceAttribute.}: Mat4 |
| 443 color {.DescriptorAttribute.}: Vec4f | 610 fontAtlas {.Descriptor.}: Texture |
| 444 | 611 other {.InstanceAttribute.}: array[3, int32] |
| 445 var p: Pipeline[ShaderInputsA] | 612 test {.Pass.}: float32 |
| 613 test1 {.PassFlat.}: Vec3f | |
| 614 color {.ShaderOutput.}: Vec4f | |
| 615 vertexCode: string = "void main() {}" | |
| 616 fragmentCode: string = "void main() {}" | |
| 617 | |
| 618 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") | |
| 620 let i = w.CreateInstance( | |
| 621 vulkanVersion = VK_MAKE_API_VERSION(0, 1, 3, 0), | |
| 622 instanceExtensions = @[], | |
| 623 layers = @["VK_LAYER_KHRONOS_validation"], | |
| 624 ) | |
| 625 | |
| 626 const (a, b) = CompileShader(Shader(A)) | |
| 627 | |
| 628 let selectedPhysicalDevice = i.GetPhysicalDevices().FilterBestGraphics() | |
| 629 let d = i.CreateDevice( | |
| 630 selectedPhysicalDevice, | |
| 631 enabledExtensions = @[], | |
| 632 selectedPhysicalDevice.FilterForGraphicsPresentationQueues() | |
| 633 ) | |
| 634 | |
| 635 var p: Pipeline[ShaderA] | |
| 446 var r: Renderable[MeshA, InstanceA] | 636 var r: Renderable[MeshA, InstanceA] |
| 447 var g: Globals | 637 var g: Globals |
| 448 var s: ShaderSet[ShaderInputsA] | 638 |
| 449 | 639 let rp = CreateRenderPass(d.vk, d.physicalDevice.GetSurfaceFormats().FilterSurfaceFormat().format) |
| 450 var p1 = CreatePipeline(device = VkDevice(0), renderPass = VkRenderPass(0), shaderSet = s) | 640 var p1 = CreatePipeline[ShaderA](device = d.vk, renderPass = rp, VkShaderModule(0), VkShaderModule(0)) |
| 451 Render(p, r, g, VkCommandBuffer(0)) | 641 Render(p, r, g, VkCommandBuffer(0)) |
