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)) |